@codeany/open-agent-sdk 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +2 -0
- package/dist/engine.js.map +1 -1
- package/package.json +5 -6
- package/src/agent.ts +0 -516
- package/src/engine.ts +0 -630
- package/src/hooks.ts +0 -261
- package/src/index.ts +0 -426
- package/src/mcp/client.ts +0 -150
- package/src/providers/anthropic.ts +0 -60
- package/src/providers/index.ts +0 -34
- package/src/providers/openai.ts +0 -315
- package/src/providers/types.ts +0 -85
- package/src/sdk-mcp-server.ts +0 -78
- package/src/session.ts +0 -227
- package/src/skills/bundled/commit.ts +0 -38
- package/src/skills/bundled/debug.ts +0 -48
- package/src/skills/bundled/index.ts +0 -28
- package/src/skills/bundled/review.ts +0 -41
- package/src/skills/bundled/simplify.ts +0 -51
- package/src/skills/bundled/test.ts +0 -43
- package/src/skills/index.ts +0 -25
- package/src/skills/registry.ts +0 -133
- package/src/skills/types.ts +0 -99
- package/src/tool-helper.ts +0 -127
- package/src/tools/agent-tool.ts +0 -164
- package/src/tools/ask-user.ts +0 -79
- package/src/tools/bash.ts +0 -75
- package/src/tools/config-tool.ts +0 -89
- package/src/tools/cron-tools.ts +0 -153
- package/src/tools/edit.ts +0 -74
- package/src/tools/glob.ts +0 -77
- package/src/tools/grep.ts +0 -168
- package/src/tools/index.ts +0 -240
- package/src/tools/lsp-tool.ts +0 -163
- package/src/tools/mcp-resource-tools.ts +0 -125
- package/src/tools/notebook-edit.ts +0 -93
- package/src/tools/plan-tools.ts +0 -88
- package/src/tools/read.ts +0 -73
- package/src/tools/send-message.ts +0 -96
- package/src/tools/skill-tool.ts +0 -133
- package/src/tools/task-tools.ts +0 -290
- package/src/tools/team-tools.ts +0 -128
- package/src/tools/todo-tool.ts +0 -112
- package/src/tools/tool-search.ts +0 -87
- package/src/tools/types.ts +0 -66
- package/src/tools/web-fetch.ts +0 -66
- package/src/tools/web-search.ts +0 -86
- package/src/tools/worktree-tools.ts +0 -140
- package/src/tools/write.ts +0 -42
- package/src/types.ts +0 -486
- package/src/utils/compact.ts +0 -207
- package/src/utils/context.ts +0 -191
- package/src/utils/fileCache.ts +0 -148
- package/src/utils/messages.ts +0 -195
- package/src/utils/retry.ts +0 -140
- package/src/utils/tokens.ts +0 -145
package/src/mcp/client.ts
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MCP Client - Connect to Model Context Protocol servers
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { ToolDefinition, McpServerConfig, ToolContext, ToolResult } from '../types.js'
|
|
6
|
-
|
|
7
|
-
export interface MCPConnection {
|
|
8
|
-
name: string
|
|
9
|
-
status: 'connected' | 'disconnected' | 'error'
|
|
10
|
-
tools: ToolDefinition[]
|
|
11
|
-
close: () => Promise<void>
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Connect to an MCP server and fetch its tools.
|
|
16
|
-
*/
|
|
17
|
-
export async function connectMCPServer(
|
|
18
|
-
name: string,
|
|
19
|
-
config: McpServerConfig,
|
|
20
|
-
): Promise<MCPConnection> {
|
|
21
|
-
try {
|
|
22
|
-
const { Client } = await import('@modelcontextprotocol/sdk/client/index.js')
|
|
23
|
-
|
|
24
|
-
let transport: any
|
|
25
|
-
|
|
26
|
-
if (!config.type || config.type === 'stdio') {
|
|
27
|
-
const stdioConfig = config as { command: string; args?: string[]; env?: Record<string, string> }
|
|
28
|
-
const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js')
|
|
29
|
-
transport = new StdioClientTransport({
|
|
30
|
-
command: stdioConfig.command,
|
|
31
|
-
args: stdioConfig.args || [],
|
|
32
|
-
env: { ...process.env, ...stdioConfig.env } as Record<string, string>,
|
|
33
|
-
})
|
|
34
|
-
} else if (config.type === 'sse') {
|
|
35
|
-
const sseConfig = config as { url: string; headers?: Record<string, string> }
|
|
36
|
-
const { SSEClientTransport } = await import('@modelcontextprotocol/sdk/client/sse.js')
|
|
37
|
-
transport = new SSEClientTransport(new URL(sseConfig.url), {
|
|
38
|
-
requestInit: sseConfig.headers ? { headers: sseConfig.headers } : undefined,
|
|
39
|
-
} as any)
|
|
40
|
-
} else if (config.type === 'http') {
|
|
41
|
-
const httpConfig = config as { url: string; headers?: Record<string, string> }
|
|
42
|
-
const { StreamableHTTPClientTransport } = await import('@modelcontextprotocol/sdk/client/streamableHttp.js')
|
|
43
|
-
transport = new StreamableHTTPClientTransport(new URL(httpConfig.url), {
|
|
44
|
-
requestInit: httpConfig.headers ? { headers: httpConfig.headers } : undefined,
|
|
45
|
-
} as any)
|
|
46
|
-
} else {
|
|
47
|
-
throw new Error(`Unsupported MCP transport type: ${(config as any).type}`)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const client = new Client(
|
|
51
|
-
{ name: `agent-sdk-${name}`, version: '1.0.0' },
|
|
52
|
-
{ capabilities: {} },
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
await client.connect(transport)
|
|
56
|
-
|
|
57
|
-
// Fetch available tools
|
|
58
|
-
const toolList = await client.listTools()
|
|
59
|
-
const tools: ToolDefinition[] = (toolList.tools || []).map((mcpTool: any) =>
|
|
60
|
-
createMCPToolDefinition(name, mcpTool, client),
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
name,
|
|
65
|
-
status: 'connected',
|
|
66
|
-
tools,
|
|
67
|
-
async close() {
|
|
68
|
-
try {
|
|
69
|
-
await client.close()
|
|
70
|
-
} catch {
|
|
71
|
-
// ignore close errors
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
}
|
|
75
|
-
} catch (err: any) {
|
|
76
|
-
console.error(`[MCP] Failed to connect to "${name}": ${err.message}`)
|
|
77
|
-
return {
|
|
78
|
-
name,
|
|
79
|
-
status: 'error',
|
|
80
|
-
tools: [],
|
|
81
|
-
async close() {},
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Create a ToolDefinition wrapping an MCP server tool.
|
|
88
|
-
*/
|
|
89
|
-
function createMCPToolDefinition(
|
|
90
|
-
serverName: string,
|
|
91
|
-
mcpTool: { name: string; description?: string; inputSchema?: any },
|
|
92
|
-
client: any,
|
|
93
|
-
): ToolDefinition {
|
|
94
|
-
const toolName = `mcp__${serverName}__${mcpTool.name}`
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
name: toolName,
|
|
98
|
-
description: mcpTool.description || `MCP tool: ${mcpTool.name} from ${serverName}`,
|
|
99
|
-
inputSchema: mcpTool.inputSchema || { type: 'object', properties: {} },
|
|
100
|
-
isReadOnly: () => false,
|
|
101
|
-
isConcurrencySafe: () => false,
|
|
102
|
-
isEnabled: () => true,
|
|
103
|
-
async prompt() {
|
|
104
|
-
return mcpTool.description || ''
|
|
105
|
-
},
|
|
106
|
-
async call(input: any): Promise<ToolResult> {
|
|
107
|
-
try {
|
|
108
|
-
const result = await client.callTool({
|
|
109
|
-
name: mcpTool.name,
|
|
110
|
-
arguments: input,
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
// Extract text content from MCP result
|
|
114
|
-
let output = ''
|
|
115
|
-
if (result.content) {
|
|
116
|
-
for (const block of result.content) {
|
|
117
|
-
if (block.type === 'text') {
|
|
118
|
-
output += block.text
|
|
119
|
-
} else {
|
|
120
|
-
output += JSON.stringify(block)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
} else {
|
|
124
|
-
output = JSON.stringify(result)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return {
|
|
128
|
-
type: 'tool_result',
|
|
129
|
-
tool_use_id: '',
|
|
130
|
-
content: output,
|
|
131
|
-
is_error: result.isError || false,
|
|
132
|
-
}
|
|
133
|
-
} catch (err: any) {
|
|
134
|
-
return {
|
|
135
|
-
type: 'tool_result',
|
|
136
|
-
tool_use_id: '',
|
|
137
|
-
content: `MCP tool error: ${err.message}`,
|
|
138
|
-
is_error: true,
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Close all MCP connections.
|
|
147
|
-
*/
|
|
148
|
-
export async function closeAllConnections(connections: MCPConnection[]): Promise<void> {
|
|
149
|
-
await Promise.allSettled(connections.map((c) => c.close()))
|
|
150
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Anthropic Messages API Provider
|
|
3
|
-
*
|
|
4
|
-
* Wraps the @anthropic-ai/sdk client. Since our internal format is
|
|
5
|
-
* Anthropic-like, this is mostly a thin pass-through.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import Anthropic from '@anthropic-ai/sdk'
|
|
9
|
-
import type {
|
|
10
|
-
LLMProvider,
|
|
11
|
-
CreateMessageParams,
|
|
12
|
-
CreateMessageResponse,
|
|
13
|
-
} from './types.js'
|
|
14
|
-
|
|
15
|
-
export class AnthropicProvider implements LLMProvider {
|
|
16
|
-
readonly apiType = 'anthropic-messages' as const
|
|
17
|
-
private client: Anthropic
|
|
18
|
-
|
|
19
|
-
constructor(opts: { apiKey?: string; baseURL?: string }) {
|
|
20
|
-
this.client = new Anthropic({
|
|
21
|
-
apiKey: opts.apiKey,
|
|
22
|
-
baseURL: opts.baseURL,
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async createMessage(params: CreateMessageParams): Promise<CreateMessageResponse> {
|
|
27
|
-
const requestParams: Anthropic.MessageCreateParamsNonStreaming = {
|
|
28
|
-
model: params.model,
|
|
29
|
-
max_tokens: params.maxTokens,
|
|
30
|
-
system: params.system,
|
|
31
|
-
messages: params.messages as Anthropic.MessageParam[],
|
|
32
|
-
tools: params.tools
|
|
33
|
-
? (params.tools as Anthropic.Tool[])
|
|
34
|
-
: undefined,
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Add extended thinking if configured
|
|
38
|
-
if (params.thinking?.type === 'enabled' && params.thinking.budget_tokens) {
|
|
39
|
-
(requestParams as any).thinking = {
|
|
40
|
-
type: 'enabled',
|
|
41
|
-
budget_tokens: params.thinking.budget_tokens,
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const response = await this.client.messages.create(requestParams)
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
content: response.content as CreateMessageResponse['content'],
|
|
49
|
-
stopReason: response.stop_reason || 'end_turn',
|
|
50
|
-
usage: {
|
|
51
|
-
input_tokens: response.usage.input_tokens,
|
|
52
|
-
output_tokens: response.usage.output_tokens,
|
|
53
|
-
cache_creation_input_tokens:
|
|
54
|
-
(response.usage as any).cache_creation_input_tokens,
|
|
55
|
-
cache_read_input_tokens:
|
|
56
|
-
(response.usage as any).cache_read_input_tokens,
|
|
57
|
-
},
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
package/src/providers/index.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM Provider Factory
|
|
3
|
-
*
|
|
4
|
-
* Creates the appropriate provider based on API type configuration.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export type { ApiType, LLMProvider, CreateMessageParams, CreateMessageResponse, NormalizedMessageParam, NormalizedContentBlock, NormalizedTool, NormalizedResponseBlock } from './types.js'
|
|
8
|
-
|
|
9
|
-
export { AnthropicProvider } from './anthropic.js'
|
|
10
|
-
export { OpenAIProvider } from './openai.js'
|
|
11
|
-
|
|
12
|
-
import type { ApiType, LLMProvider } from './types.js'
|
|
13
|
-
import { AnthropicProvider } from './anthropic.js'
|
|
14
|
-
import { OpenAIProvider } from './openai.js'
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Create an LLM provider based on the API type.
|
|
18
|
-
*
|
|
19
|
-
* @param apiType - 'anthropic-messages' or 'openai-completions'
|
|
20
|
-
* @param opts - API credentials
|
|
21
|
-
*/
|
|
22
|
-
export function createProvider(
|
|
23
|
-
apiType: ApiType,
|
|
24
|
-
opts: { apiKey?: string; baseURL?: string },
|
|
25
|
-
): LLMProvider {
|
|
26
|
-
switch (apiType) {
|
|
27
|
-
case 'anthropic-messages':
|
|
28
|
-
return new AnthropicProvider(opts)
|
|
29
|
-
case 'openai-completions':
|
|
30
|
-
return new OpenAIProvider(opts)
|
|
31
|
-
default:
|
|
32
|
-
throw new Error(`Unsupported API type: ${apiType}. Use 'anthropic-messages' or 'openai-completions'.`)
|
|
33
|
-
}
|
|
34
|
-
}
|
package/src/providers/openai.ts
DELETED
|
@@ -1,315 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenAI Chat Completions API Provider
|
|
3
|
-
*
|
|
4
|
-
* Converts between the SDK's internal Anthropic-like message format
|
|
5
|
-
* and OpenAI's Chat Completions API format.
|
|
6
|
-
*
|
|
7
|
-
* Uses native fetch (no openai SDK dependency required).
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import type {
|
|
11
|
-
LLMProvider,
|
|
12
|
-
CreateMessageParams,
|
|
13
|
-
CreateMessageResponse,
|
|
14
|
-
NormalizedMessageParam,
|
|
15
|
-
NormalizedContentBlock,
|
|
16
|
-
NormalizedTool,
|
|
17
|
-
NormalizedResponseBlock,
|
|
18
|
-
} from './types.js'
|
|
19
|
-
|
|
20
|
-
// --------------------------------------------------------------------------
|
|
21
|
-
// OpenAI-specific types (minimal, just what we need)
|
|
22
|
-
// --------------------------------------------------------------------------
|
|
23
|
-
|
|
24
|
-
interface OpenAIChatMessage {
|
|
25
|
-
role: 'system' | 'user' | 'assistant' | 'tool'
|
|
26
|
-
content?: string | null
|
|
27
|
-
tool_calls?: OpenAIToolCall[]
|
|
28
|
-
tool_call_id?: string
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface OpenAIToolCall {
|
|
32
|
-
id: string
|
|
33
|
-
type: 'function'
|
|
34
|
-
function: {
|
|
35
|
-
name: string
|
|
36
|
-
arguments: string
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
interface OpenAITool {
|
|
41
|
-
type: 'function'
|
|
42
|
-
function: {
|
|
43
|
-
name: string
|
|
44
|
-
description: string
|
|
45
|
-
parameters: Record<string, any>
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
interface OpenAIChatResponse {
|
|
50
|
-
id: string
|
|
51
|
-
choices: Array<{
|
|
52
|
-
index: number
|
|
53
|
-
message: {
|
|
54
|
-
role: 'assistant'
|
|
55
|
-
content: string | null
|
|
56
|
-
tool_calls?: OpenAIToolCall[]
|
|
57
|
-
}
|
|
58
|
-
finish_reason: 'stop' | 'length' | 'tool_calls' | 'content_filter' | string
|
|
59
|
-
}>
|
|
60
|
-
usage?: {
|
|
61
|
-
prompt_tokens: number
|
|
62
|
-
completion_tokens: number
|
|
63
|
-
total_tokens: number
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// --------------------------------------------------------------------------
|
|
68
|
-
// Provider
|
|
69
|
-
// --------------------------------------------------------------------------
|
|
70
|
-
|
|
71
|
-
export class OpenAIProvider implements LLMProvider {
|
|
72
|
-
readonly apiType = 'openai-completions' as const
|
|
73
|
-
private apiKey: string
|
|
74
|
-
private baseURL: string
|
|
75
|
-
|
|
76
|
-
constructor(opts: { apiKey?: string; baseURL?: string }) {
|
|
77
|
-
this.apiKey = opts.apiKey || ''
|
|
78
|
-
this.baseURL = (opts.baseURL || 'https://api.openai.com/v1').replace(/\/$/, '')
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async createMessage(params: CreateMessageParams): Promise<CreateMessageResponse> {
|
|
82
|
-
// Convert to OpenAI format
|
|
83
|
-
const messages = this.convertMessages(params.system, params.messages)
|
|
84
|
-
const tools = params.tools ? this.convertTools(params.tools) : undefined
|
|
85
|
-
|
|
86
|
-
const body: Record<string, any> = {
|
|
87
|
-
model: params.model,
|
|
88
|
-
max_tokens: params.maxTokens,
|
|
89
|
-
messages,
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (tools && tools.length > 0) {
|
|
93
|
-
body.tools = tools
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Make API call
|
|
97
|
-
const response = await fetch(`${this.baseURL}/chat/completions`, {
|
|
98
|
-
method: 'POST',
|
|
99
|
-
headers: {
|
|
100
|
-
'Content-Type': 'application/json',
|
|
101
|
-
Authorization: `Bearer ${this.apiKey}`,
|
|
102
|
-
},
|
|
103
|
-
body: JSON.stringify(body),
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
if (!response.ok) {
|
|
107
|
-
const errBody = await response.text().catch(() => '')
|
|
108
|
-
const err: any = new Error(
|
|
109
|
-
`OpenAI API error: ${response.status} ${response.statusText}: ${errBody}`,
|
|
110
|
-
)
|
|
111
|
-
err.status = response.status
|
|
112
|
-
throw err
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const data = (await response.json()) as OpenAIChatResponse
|
|
116
|
-
|
|
117
|
-
// Convert response back to normalized format
|
|
118
|
-
return this.convertResponse(data)
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// --------------------------------------------------------------------------
|
|
122
|
-
// Message Conversion: Internal → OpenAI
|
|
123
|
-
// --------------------------------------------------------------------------
|
|
124
|
-
|
|
125
|
-
private convertMessages(
|
|
126
|
-
system: string,
|
|
127
|
-
messages: NormalizedMessageParam[],
|
|
128
|
-
): OpenAIChatMessage[] {
|
|
129
|
-
const result: OpenAIChatMessage[] = []
|
|
130
|
-
|
|
131
|
-
// System prompt as first message
|
|
132
|
-
if (system) {
|
|
133
|
-
result.push({ role: 'system', content: system })
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
for (const msg of messages) {
|
|
137
|
-
if (msg.role === 'user') {
|
|
138
|
-
this.convertUserMessage(msg, result)
|
|
139
|
-
} else if (msg.role === 'assistant') {
|
|
140
|
-
this.convertAssistantMessage(msg, result)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return result
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
private convertUserMessage(
|
|
148
|
-
msg: NormalizedMessageParam,
|
|
149
|
-
result: OpenAIChatMessage[],
|
|
150
|
-
): void {
|
|
151
|
-
if (typeof msg.content === 'string') {
|
|
152
|
-
result.push({ role: 'user', content: msg.content })
|
|
153
|
-
return
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Content blocks may contain text and/or tool_result blocks
|
|
157
|
-
const textParts: string[] = []
|
|
158
|
-
const toolResults: Array<{ tool_use_id: string; content: string }> = []
|
|
159
|
-
|
|
160
|
-
for (const block of msg.content) {
|
|
161
|
-
if (block.type === 'text') {
|
|
162
|
-
textParts.push(block.text)
|
|
163
|
-
} else if (block.type === 'tool_result') {
|
|
164
|
-
toolResults.push({
|
|
165
|
-
tool_use_id: block.tool_use_id,
|
|
166
|
-
content: block.content,
|
|
167
|
-
})
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Tool results become separate tool messages
|
|
172
|
-
for (const tr of toolResults) {
|
|
173
|
-
result.push({
|
|
174
|
-
role: 'tool',
|
|
175
|
-
tool_call_id: tr.tool_use_id,
|
|
176
|
-
content: tr.content,
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Text parts become a user message
|
|
181
|
-
if (textParts.length > 0) {
|
|
182
|
-
result.push({ role: 'user', content: textParts.join('\n') })
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
private convertAssistantMessage(
|
|
187
|
-
msg: NormalizedMessageParam,
|
|
188
|
-
result: OpenAIChatMessage[],
|
|
189
|
-
): void {
|
|
190
|
-
if (typeof msg.content === 'string') {
|
|
191
|
-
result.push({ role: 'assistant', content: msg.content })
|
|
192
|
-
return
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Extract text and tool_use blocks
|
|
196
|
-
const textParts: string[] = []
|
|
197
|
-
const toolCalls: OpenAIToolCall[] = []
|
|
198
|
-
|
|
199
|
-
for (const block of msg.content) {
|
|
200
|
-
if (block.type === 'text') {
|
|
201
|
-
textParts.push(block.text)
|
|
202
|
-
} else if (block.type === 'tool_use') {
|
|
203
|
-
toolCalls.push({
|
|
204
|
-
id: block.id,
|
|
205
|
-
type: 'function',
|
|
206
|
-
function: {
|
|
207
|
-
name: block.name,
|
|
208
|
-
arguments: typeof block.input === 'string'
|
|
209
|
-
? block.input
|
|
210
|
-
: JSON.stringify(block.input),
|
|
211
|
-
},
|
|
212
|
-
})
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const assistantMsg: OpenAIChatMessage = {
|
|
217
|
-
role: 'assistant',
|
|
218
|
-
content: textParts.length > 0 ? textParts.join('\n') : null,
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (toolCalls.length > 0) {
|
|
222
|
-
assistantMsg.tool_calls = toolCalls
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
result.push(assistantMsg)
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// --------------------------------------------------------------------------
|
|
229
|
-
// Tool Conversion: Internal → OpenAI
|
|
230
|
-
// --------------------------------------------------------------------------
|
|
231
|
-
|
|
232
|
-
private convertTools(tools: NormalizedTool[]): OpenAITool[] {
|
|
233
|
-
return tools.map((t) => ({
|
|
234
|
-
type: 'function' as const,
|
|
235
|
-
function: {
|
|
236
|
-
name: t.name,
|
|
237
|
-
description: t.description,
|
|
238
|
-
parameters: t.input_schema,
|
|
239
|
-
},
|
|
240
|
-
}))
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// --------------------------------------------------------------------------
|
|
244
|
-
// Response Conversion: OpenAI → Internal
|
|
245
|
-
// --------------------------------------------------------------------------
|
|
246
|
-
|
|
247
|
-
private convertResponse(data: OpenAIChatResponse): CreateMessageResponse {
|
|
248
|
-
const choice = data.choices[0]
|
|
249
|
-
if (!choice) {
|
|
250
|
-
return {
|
|
251
|
-
content: [{ type: 'text', text: '' }],
|
|
252
|
-
stopReason: 'end_turn',
|
|
253
|
-
usage: { input_tokens: 0, output_tokens: 0 },
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const content: NormalizedResponseBlock[] = []
|
|
258
|
-
|
|
259
|
-
// Add text content
|
|
260
|
-
if (choice.message.content) {
|
|
261
|
-
content.push({ type: 'text', text: choice.message.content })
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Add tool calls
|
|
265
|
-
if (choice.message.tool_calls) {
|
|
266
|
-
for (const tc of choice.message.tool_calls) {
|
|
267
|
-
let input: any
|
|
268
|
-
try {
|
|
269
|
-
input = JSON.parse(tc.function.arguments)
|
|
270
|
-
} catch {
|
|
271
|
-
input = tc.function.arguments
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
content.push({
|
|
275
|
-
type: 'tool_use',
|
|
276
|
-
id: tc.id,
|
|
277
|
-
name: tc.function.name,
|
|
278
|
-
input,
|
|
279
|
-
})
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// If no content at all, add empty text
|
|
284
|
-
if (content.length === 0) {
|
|
285
|
-
content.push({ type: 'text', text: '' })
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// Map finish_reason to our normalized stop reasons
|
|
289
|
-
const stopReason = this.mapFinishReason(choice.finish_reason)
|
|
290
|
-
|
|
291
|
-
return {
|
|
292
|
-
content,
|
|
293
|
-
stopReason,
|
|
294
|
-
usage: {
|
|
295
|
-
input_tokens: data.usage?.prompt_tokens || 0,
|
|
296
|
-
output_tokens: data.usage?.completion_tokens || 0,
|
|
297
|
-
},
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
private mapFinishReason(
|
|
302
|
-
reason: string,
|
|
303
|
-
): 'end_turn' | 'max_tokens' | 'tool_use' | string {
|
|
304
|
-
switch (reason) {
|
|
305
|
-
case 'stop':
|
|
306
|
-
return 'end_turn'
|
|
307
|
-
case 'length':
|
|
308
|
-
return 'max_tokens'
|
|
309
|
-
case 'tool_calls':
|
|
310
|
-
return 'tool_use'
|
|
311
|
-
default:
|
|
312
|
-
return reason
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
package/src/providers/types.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LLM Provider Abstraction Types
|
|
3
|
-
*
|
|
4
|
-
* Defines a provider interface that normalizes API differences between
|
|
5
|
-
* Anthropic Messages API and OpenAI Chat Completions API.
|
|
6
|
-
*
|
|
7
|
-
* Internally the SDK uses Anthropic-like message format as the canonical
|
|
8
|
-
* representation. Providers convert to/from their native API format.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// --------------------------------------------------------------------------
|
|
12
|
-
// API Type
|
|
13
|
-
// --------------------------------------------------------------------------
|
|
14
|
-
|
|
15
|
-
export type ApiType = 'anthropic-messages' | 'openai-completions'
|
|
16
|
-
|
|
17
|
-
// --------------------------------------------------------------------------
|
|
18
|
-
// Normalized Request
|
|
19
|
-
// --------------------------------------------------------------------------
|
|
20
|
-
|
|
21
|
-
export interface CreateMessageParams {
|
|
22
|
-
model: string
|
|
23
|
-
maxTokens: number
|
|
24
|
-
system: string
|
|
25
|
-
messages: NormalizedMessageParam[]
|
|
26
|
-
tools?: NormalizedTool[]
|
|
27
|
-
thinking?: { type: string; budget_tokens?: number }
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Normalized message format (Anthropic-like).
|
|
32
|
-
* This is the internal representation used throughout the SDK.
|
|
33
|
-
*/
|
|
34
|
-
export interface NormalizedMessageParam {
|
|
35
|
-
role: 'user' | 'assistant'
|
|
36
|
-
content: string | NormalizedContentBlock[]
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export type NormalizedContentBlock =
|
|
40
|
-
| { type: 'text'; text: string }
|
|
41
|
-
| { type: 'tool_use'; id: string; name: string; input: any }
|
|
42
|
-
| { type: 'tool_result'; tool_use_id: string; content: string; is_error?: boolean }
|
|
43
|
-
| { type: 'image'; source: any }
|
|
44
|
-
| { type: 'thinking'; thinking: string }
|
|
45
|
-
|
|
46
|
-
export interface NormalizedTool {
|
|
47
|
-
name: string
|
|
48
|
-
description: string
|
|
49
|
-
input_schema: {
|
|
50
|
-
type: 'object'
|
|
51
|
-
properties: Record<string, any>
|
|
52
|
-
required?: string[]
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// --------------------------------------------------------------------------
|
|
57
|
-
// Normalized Response
|
|
58
|
-
// --------------------------------------------------------------------------
|
|
59
|
-
|
|
60
|
-
export interface CreateMessageResponse {
|
|
61
|
-
content: NormalizedResponseBlock[]
|
|
62
|
-
stopReason: 'end_turn' | 'max_tokens' | 'tool_use' | string
|
|
63
|
-
usage: {
|
|
64
|
-
input_tokens: number
|
|
65
|
-
output_tokens: number
|
|
66
|
-
cache_creation_input_tokens?: number
|
|
67
|
-
cache_read_input_tokens?: number
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export type NormalizedResponseBlock =
|
|
72
|
-
| { type: 'text'; text: string }
|
|
73
|
-
| { type: 'tool_use'; id: string; name: string; input: any }
|
|
74
|
-
|
|
75
|
-
// --------------------------------------------------------------------------
|
|
76
|
-
// Provider Interface
|
|
77
|
-
// --------------------------------------------------------------------------
|
|
78
|
-
|
|
79
|
-
export interface LLMProvider {
|
|
80
|
-
/** The API type this provider implements. */
|
|
81
|
-
readonly apiType: ApiType
|
|
82
|
-
|
|
83
|
-
/** Send a message and get a response. */
|
|
84
|
-
createMessage(params: CreateMessageParams): Promise<CreateMessageResponse>
|
|
85
|
-
}
|