Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions src/providers/ai-sdk-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* Type definitions for AI SDK provider
*/

import { LanguageModel, JSONValue } from 'ai';

export interface AiSdkFunctionTool {
type: 'function';
function: {
name: string;
description?: string;
parameters: Record<string, unknown>;
};
}

export interface ToolCallFunction {
name: string;
arguments: string | Record<string, unknown>;
}

export interface ToolCall {
id: string;
type: 'function';
function: ToolCallFunction;
}

export type AiSdkChatMessageParam =
| { role: 'system'; content: string }
| {
role: 'user' | 'assistant' | 'tool';
content: string | null;
tool_calls?: ToolCall[];
tool_call_id?: string;
};

export interface AiSdkChatRequest {
model: string;
messages: AiSdkChatMessageParam[];
temperature?: number;
max_tokens?: number;
maxTokens?: number;
tools?: AiSdkFunctionTool[];
tool_choice?: 'auto' | 'none' | { type: 'function'; function: { name: string } };
response_format?: { type: 'json_object' };
[key: string]: unknown;
}

export interface Usage {
prompt_tokens?: number;
completion_tokens?: number;
total_tokens?: number;
}

export interface AiSdkChatResponse {
message?: {
content?: string | null;
tool_calls?: ToolCall[];
};
choices?: Array<{
message?: {
content?: string | null;
tool_calls?: ToolCall[];
};
}>;
text?: string | null;
id?: string;
model?: string;
created?: number;
usage?: Usage;
[key: string]: unknown;
}

export interface AiSdkClient {
chat: (request: AiSdkChatRequest) => Promise<AiSdkChatResponse>;
}

export interface GenerateObjectResult {
object: unknown;
}

export interface GenerateObjectOptions {
model: LanguageModel;
schema: unknown;
system?: string;
messages: unknown[];
temperature?: number;
maxOutputTokens?: number;
}

export type SafeJsonParseResult = JSONValue;
122 changes: 26 additions & 96 deletions src/providers/ai-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,105 +13,37 @@ import {
zodSchema,
} from 'ai';
import { ModelProvider, Message, getTextContent } from '../core/types.js';

export type AiSdkFunctionTool = {
type: 'function';
function: {
name: string;
description?: string;
parameters: unknown;
};
};

function safeParseJson(text: string): JSONValue {
import {
AiSdkFunctionTool,
AiSdkChatMessageParam,
AiSdkChatRequest,
AiSdkChatResponse,
AiSdkClient,
SafeJsonParseResult,
GenerateObjectResult,
GenerateObjectOptions
} from './ai-sdk-types.js';

function safeParseJson(text: string): SafeJsonParseResult {
try {
return JSON.parse(text) as JSONValue;
} catch {
return text;
}
}

export type AiSdkChatMessageParam =
| { role: 'system'; content: string }
| {
role: 'user' | 'assistant' | 'tool';
content: string | null;
tool_calls?: Array<{
id: string;
type: 'function';
function: {
name: string;
arguments: string | any;
};
}>;
tool_call_id?: string;
};

export type AiSdkChatRequest = {
model: string;
messages: AiSdkChatMessageParam[];
temperature?: number;
// Support both OpenAI-style and AI SDK-style naming for token limits
max_tokens?: number;
maxTokens?: number;
tools?: AiSdkFunctionTool[];
tool_choice?: 'auto' | 'none' | { type: 'function'; function: { name: string } };
response_format?: { type: 'json_object' };
// Allow arbitrary provider-specific fields
[key: string]: unknown;
export {
AiSdkFunctionTool,
AiSdkChatMessageParam,
AiSdkChatRequest,
AiSdkChatResponse,
AiSdkClient
};

export type AiSdkChatResponse = {
// Prefer a single normalized message if provided by the client
message?: {
content?: string | null;
tool_calls?: Array<{
id: string;
type: 'function';
function: {
name: string;
arguments: string | any;
};
}>;
};
// Fallbacks for OpenAI-compatible responses
choices?: Array<{
message?: {
content?: string | null;
tool_calls?: Array<{
id: string;
type: 'function';
function: {
name: string;
arguments: string | any;
};
}>;
};
}>;
// Fallback for plain-text responses (e.g., ai SDK generateText)
text?: string | null;

// Optional metadata if available
id?: string;
model?: string;
created?: number;
usage?: {
prompt_tokens?: number;
completion_tokens?: number;
total_tokens?: number;
};

[key: string]: unknown;
};

export interface AiSdkClient {
chat: (request: AiSdkChatRequest) => Promise<AiSdkChatResponse>;
}

export const createAiSdkProvider = <Ctx>(
model: unknown,
model: LanguageModel,
): ModelProvider<Ctx> => {
const lm = model as LanguageModel;
const lm = model;
return {
async getCompletion(state, agent) {
const system = agent.instructions(state);
Expand Down Expand Up @@ -170,7 +102,7 @@ export const createAiSdkProvider = <Ctx>(
!hasCompletedTools && agent.tools && agent.tools.length > 0
? agent.tools.reduce(
(acc, jafTool) => {
const toSchema = zodSchema as unknown as (s: unknown) => Schema;
const toSchema = zodSchema as (s: unknown) => Schema;
acc[jafTool.schema.name] = tool({
description: jafTool.schema.description,
inputSchema: toSchema(jafTool.schema.parameters),
Expand All @@ -184,19 +116,17 @@ export const createAiSdkProvider = <Ctx>(
const shouldGenerateObject = Boolean(agent.outputCodec) && !toolsForAiSDK;

if (shouldGenerateObject) {
const toSchema = zodSchema as unknown as (s: unknown) => Schema;
const go = generateObject as unknown as (opts: unknown) => Promise<unknown>;
const resultUnknown = await go({
const toSchema = zodSchema as (s: unknown) => Schema;
const result = await generateObject({
model: lm,
schema: toSchema((agent.outputCodec as unknown) as import('zod').ZodType<unknown>),
schema: toSchema(agent.outputCodec as import('zod').ZodType<unknown>),
system,
messages,
temperature: agent.modelConfig?.temperature,
maxOutputTokens: agent.modelConfig?.maxTokens,
});
const object = (resultUnknown as { object: unknown }).object;
}) as GenerateObjectResult;

return { message: { content: JSON.stringify(object) } };
return { message: { content: JSON.stringify(result.object) } };
}

console.log(`[DEBUG] Tools passed to AI SDK: ${toolsForAiSDK ? Object.keys(toolsForAiSDK).length : 0} (hasCompletedTools: ${hasCompletedTools})`);
Expand Down
18 changes: 18 additions & 0 deletions src/providers/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Constants for model providers
*/

export const VISION_MODEL_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
export const VISION_API_TIMEOUT = 3000; // 3 seconds

export const KNOWN_VISION_MODELS = [
'gpt-4-vision-preview',
'gpt-4o',
'gpt-4o-mini',
'claude-sonnet-4',
'claude-sonnet-4-20250514',
'gemini-2.5-flash',
'gemini-2.5-pro'
] as const;

export type KnownVisionModel = typeof KNOWN_VISION_MODELS[number];
26 changes: 26 additions & 0 deletions src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,29 @@ export {
type AiSdkClient,
} from './ai-sdk';

// Export type definitions for external use
export type {
ProxyConfig,
ProxyAgentResult,
ClientConfig,
JsonSchema,
VisionModelInfo,
VisionApiResponse,
VisionModelCacheEntry
} from './types';

export type {
MCPClient,
MCPToolDefinition,
MCPClientOptions
} from './mcp-types';

export type {
ToolCall,
ToolCallFunction,
Usage,
GenerateObjectResult,
GenerateObjectOptions,
SafeJsonParseResult
} from './ai-sdk-types';

61 changes: 61 additions & 0 deletions src/providers/mcp-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* Type definitions for MCP (Model Context Protocol) provider
*/

import { z } from 'zod';

export interface JsonSchema {
type?: string;
properties?: Record<string, JsonSchema>;
required?: string[];
description?: string;
enum?: string[];
items?: JsonSchema;
[key: string]: unknown;
}

export interface MCPToolDefinition {
name: string;
description?: string;
inputSchema?: JsonSchema;
}

export interface MCPContentItem {
type: string;
text?: string;
[key: string]: unknown;
}

export interface MCPToolResponse {
content?: MCPContentItem[];
[key: string]: unknown;
}

export interface MCPClient {
listTools(): Promise<MCPToolDefinition[]>;
callTool(name: string, args: unknown): Promise<string>;
close(): Promise<void>;
}

export interface MCPClientOptions {
headers?: Record<string, string>;
sessionId?: string;
fetch?: typeof fetch;
requestInit?: RequestInit;
}

export interface EventSourceModule {
default?: unknown;
[key: string]: unknown;
}

export type ZodSchemaType = z.ZodType<unknown>;

export interface MCPToolConversionResult<Ctx> {
schema: {
name: string;
description: string;
parameters: z.ZodObject<Record<string, ZodSchemaType>>;
};
execute: (args: unknown, ctx: Ctx) => Promise<string>;
}
Loading