cowork-os 0.3.21 → 0.3.25
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/README.md +372 -10
- package/connectors/README.md +20 -0
- package/connectors/asana-mcp/README.md +24 -0
- package/connectors/asana-mcp/dist/index.js +427 -0
- package/connectors/asana-mcp/package.json +15 -0
- package/connectors/asana-mcp/src/index.ts +553 -0
- package/connectors/asana-mcp/tsconfig.json +13 -0
- package/connectors/hubspot-mcp/README.md +35 -0
- package/connectors/hubspot-mcp/dist/index.js +454 -0
- package/connectors/hubspot-mcp/package.json +15 -0
- package/connectors/hubspot-mcp/src/index.ts +562 -0
- package/connectors/hubspot-mcp/tsconfig.json +13 -0
- package/connectors/jira-mcp/README.md +49 -0
- package/connectors/jira-mcp/dist/index.js +588 -0
- package/connectors/jira-mcp/package.json +15 -0
- package/connectors/jira-mcp/src/index.ts +711 -0
- package/connectors/jira-mcp/tsconfig.json +13 -0
- package/connectors/linear-mcp/README.md +22 -0
- package/connectors/linear-mcp/dist/index.js +402 -0
- package/connectors/linear-mcp/package.json +15 -0
- package/connectors/linear-mcp/src/index.ts +522 -0
- package/connectors/linear-mcp/tsconfig.json +13 -0
- package/connectors/okta-mcp/README.md +24 -0
- package/connectors/okta-mcp/dist/index.js +411 -0
- package/connectors/okta-mcp/package.json +15 -0
- package/connectors/okta-mcp/src/index.ts +520 -0
- package/connectors/okta-mcp/tsconfig.json +13 -0
- package/connectors/salesforce-mcp/README.md +47 -0
- package/connectors/salesforce-mcp/dist/index.js +584 -0
- package/connectors/salesforce-mcp/package.json +15 -0
- package/connectors/salesforce-mcp/src/index.ts +722 -0
- package/connectors/salesforce-mcp/tsconfig.json +13 -0
- package/connectors/servicenow-mcp/README.md +26 -0
- package/connectors/servicenow-mcp/dist/index.js +400 -0
- package/connectors/servicenow-mcp/package.json +15 -0
- package/connectors/servicenow-mcp/src/index.ts +500 -0
- package/connectors/servicenow-mcp/tsconfig.json +13 -0
- package/connectors/templates/mcp-connector/README.md +31 -0
- package/connectors/templates/mcp-connector/package.json +15 -0
- package/connectors/templates/mcp-connector/src/index.ts +330 -0
- package/connectors/templates/mcp-connector/tsconfig.json +13 -0
- package/connectors/zendesk-mcp/README.md +40 -0
- package/connectors/zendesk-mcp/dist/index.js +431 -0
- package/connectors/zendesk-mcp/package.json +15 -0
- package/connectors/zendesk-mcp/src/index.ts +543 -0
- package/connectors/zendesk-mcp/tsconfig.json +13 -0
- package/dist/electron/electron/agent/custom-skill-loader.js +31 -1
- package/dist/electron/electron/agent/daemon.js +189 -13
- package/dist/electron/electron/agent/executor.js +895 -78
- package/dist/electron/electron/agent/llm/anthropic-compatible-provider.js +177 -0
- package/dist/electron/electron/agent/llm/azure-openai-provider.js +328 -0
- package/dist/electron/electron/agent/llm/bedrock-provider.js +49 -9
- package/dist/electron/electron/agent/llm/github-copilot-provider.js +97 -0
- package/dist/electron/electron/agent/llm/groq-provider.js +33 -0
- package/dist/electron/electron/agent/llm/index.js +13 -1
- package/dist/electron/electron/agent/llm/kimi-provider.js +33 -0
- package/dist/electron/electron/agent/llm/openai-compatible-provider.js +116 -0
- package/dist/electron/electron/agent/llm/openai-compatible.js +111 -0
- package/dist/electron/electron/agent/llm/openai-oauth.js +2 -1
- package/dist/electron/electron/agent/llm/openrouter-provider.js +1 -1
- package/dist/electron/electron/agent/llm/provider-factory.js +350 -4
- package/dist/electron/electron/agent/llm/types.js +66 -1
- package/dist/electron/electron/agent/llm/xai-provider.js +33 -0
- package/dist/electron/electron/agent/search/provider-factory.js +38 -2
- package/dist/electron/electron/agent/tools/box-tools.js +231 -0
- package/dist/electron/electron/agent/tools/builtin-settings.js +28 -0
- package/dist/electron/electron/agent/tools/dropbox-tools.js +237 -0
- package/dist/electron/electron/agent/tools/file-tools.js +66 -3
- package/dist/electron/electron/agent/tools/google-drive-tools.js +227 -0
- package/dist/electron/electron/agent/tools/grep-tools.js +90 -10
- package/dist/electron/electron/agent/tools/image-tools.js +11 -1
- package/dist/electron/electron/agent/tools/notion-tools.js +312 -0
- package/dist/electron/electron/agent/tools/onedrive-tools.js +217 -0
- package/dist/electron/electron/agent/tools/registry.js +548 -10
- package/dist/electron/electron/agent/tools/search-tools.js +28 -10
- package/dist/electron/electron/agent/tools/sharepoint-tools.js +243 -0
- package/dist/electron/electron/agent/tools/shell-tools.js +12 -3
- package/dist/electron/electron/agent/tools/x-tools.js +1 -1
- package/dist/electron/electron/agents/agent-dispatch.js +63 -0
- package/dist/electron/electron/database/repositories.js +19 -5
- package/dist/electron/electron/database/schema.js +8 -0
- package/dist/electron/electron/gateway/channels/whatsapp.js +55 -0
- package/dist/electron/electron/gateway/index.js +75 -1
- package/dist/electron/electron/gateway/router.js +209 -154
- package/dist/electron/electron/ipc/canvas-handlers.js +5 -0
- package/dist/electron/electron/ipc/handlers.js +763 -267
- package/dist/electron/electron/main.js +63 -0
- package/dist/electron/electron/mcp/oauth/connector-oauth.js +333 -0
- package/dist/electron/electron/mcp/registry/MCPRegistryManager.js +503 -154
- package/dist/electron/electron/memory/MemoryService.js +2 -1
- package/dist/electron/electron/preload.js +78 -1
- package/dist/electron/electron/settings/appearance-manager.js +18 -1
- package/dist/electron/electron/settings/box-manager.js +54 -0
- package/dist/electron/electron/settings/dropbox-manager.js +54 -0
- package/dist/electron/electron/settings/google-drive-manager.js +54 -0
- package/dist/electron/electron/settings/notion-manager.js +56 -0
- package/dist/electron/electron/settings/onedrive-manager.js +54 -0
- package/dist/electron/electron/settings/sharepoint-manager.js +54 -0
- package/dist/electron/electron/utils/box-api.js +153 -0
- package/dist/electron/electron/utils/dropbox-api.js +144 -0
- package/dist/electron/electron/utils/env-migration.js +19 -0
- package/dist/electron/electron/utils/google-drive-api.js +152 -0
- package/dist/electron/electron/utils/notion-api.js +103 -0
- package/dist/electron/electron/utils/onedrive-api.js +113 -0
- package/dist/electron/electron/utils/sharepoint-api.js +109 -0
- package/dist/electron/electron/utils/validation.js +98 -3
- package/dist/electron/electron/utils/x-cli.js +1 -1
- package/dist/electron/shared/channelMessages.js +284 -3
- package/dist/electron/shared/llm-provider-catalog.js +198 -0
- package/dist/electron/shared/types.js +90 -1
- package/package.json +14 -3
- package/resources/skills/nano-banana-pro.json +4 -4
- package/resources/skills/openai-image-gen.json +3 -3
- package/resources/skills/scripts/gen.py +163 -0
- package/resources/skills/scripts/generate_image.py +91 -0
- package/src/electron/agent/custom-skill-loader.ts +34 -1
- package/src/electron/agent/daemon.ts +210 -14
- package/src/electron/agent/executor.ts +1124 -85
- package/src/electron/agent/llm/anthropic-compatible-provider.ts +214 -0
- package/src/electron/agent/llm/azure-openai-provider.ts +388 -0
- package/src/electron/agent/llm/bedrock-provider.ts +62 -9
- package/src/electron/agent/llm/github-copilot-provider.ts +117 -0
- package/src/electron/agent/llm/groq-provider.ts +39 -0
- package/src/electron/agent/llm/index.ts +6 -0
- package/src/electron/agent/llm/kimi-provider.ts +39 -0
- package/src/electron/agent/llm/openai-compatible-provider.ts +153 -0
- package/src/electron/agent/llm/openai-compatible.ts +133 -0
- package/src/electron/agent/llm/openai-oauth.ts +2 -1
- package/src/electron/agent/llm/openrouter-provider.ts +2 -1
- package/src/electron/agent/llm/provider-factory.ts +459 -6
- package/src/electron/agent/llm/types.ts +95 -1
- package/src/electron/agent/llm/xai-provider.ts +39 -0
- package/src/electron/agent/search/provider-factory.ts +43 -2
- package/src/electron/agent/tools/box-tools.ts +239 -0
- package/src/electron/agent/tools/builtin-settings.ts +36 -0
- package/src/electron/agent/tools/dropbox-tools.ts +237 -0
- package/src/electron/agent/tools/file-tools.ts +66 -3
- package/src/electron/agent/tools/gmail-tools.ts +240 -0
- package/src/electron/agent/tools/google-calendar-tools.ts +258 -0
- package/src/electron/agent/tools/google-drive-tools.ts +228 -0
- package/src/electron/agent/tools/grep-tools.ts +97 -12
- package/src/electron/agent/tools/image-tools.ts +11 -1
- package/src/electron/agent/tools/notion-tools.ts +330 -0
- package/src/electron/agent/tools/onedrive-tools.ts +217 -0
- package/src/electron/agent/tools/registry.ts +794 -10
- package/src/electron/agent/tools/search-tools.ts +29 -11
- package/src/electron/agent/tools/sharepoint-tools.ts +247 -0
- package/src/electron/agent/tools/shell-tools.ts +11 -3
- package/src/electron/agent/tools/x-tools.ts +1 -1
- package/src/electron/agents/agent-dispatch.ts +79 -0
- package/src/electron/database/SecureSettingsRepository.ts +7 -1
- package/src/electron/database/repositories.ts +58 -6
- package/src/electron/database/schema.ts +8 -0
- package/src/electron/gateway/channels/discord.ts +4 -0
- package/src/electron/gateway/channels/google-chat.ts +3 -0
- package/src/electron/gateway/channels/line.ts +3 -0
- package/src/electron/gateway/channels/matrix-client.ts +15 -0
- package/src/electron/gateway/channels/matrix.ts +31 -0
- package/src/electron/gateway/channels/mattermost.ts +3 -0
- package/src/electron/gateway/channels/signal.ts +3 -0
- package/src/electron/gateway/channels/slack.ts +9 -4
- package/src/electron/gateway/channels/teams.ts +4 -0
- package/src/electron/gateway/channels/telegram.ts +2 -0
- package/src/electron/gateway/channels/twitch.ts +2 -0
- package/src/electron/gateway/channels/types.ts +8 -0
- package/src/electron/gateway/channels/whatsapp.ts +66 -0
- package/src/electron/gateway/index.ts +95 -2
- package/src/electron/gateway/router.ts +231 -161
- package/src/electron/gateway/security.ts +21 -9
- package/src/electron/ipc/canvas-handlers.ts +10 -0
- package/src/electron/ipc/handlers.ts +848 -292
- package/src/electron/main.ts +35 -0
- package/src/electron/mcp/oauth/connector-oauth.ts +448 -0
- package/src/electron/mcp/registry/MCPRegistryManager.ts +343 -12
- package/src/electron/memory/MemoryService.ts +7 -1
- package/src/electron/preload.ts +200 -5
- package/src/electron/settings/appearance-manager.ts +20 -2
- package/src/electron/settings/box-manager.ts +58 -0
- package/src/electron/settings/dropbox-manager.ts +58 -0
- package/src/electron/settings/google-workspace-manager.ts +59 -0
- package/src/electron/settings/notion-manager.ts +60 -0
- package/src/electron/settings/onedrive-manager.ts +58 -0
- package/src/electron/settings/sharepoint-manager.ts +58 -0
- package/src/electron/utils/box-api.ts +184 -0
- package/src/electron/utils/dropbox-api.ts +171 -0
- package/src/electron/utils/env-migration.ts +22 -0
- package/src/electron/utils/gmail-api.ts +121 -0
- package/src/electron/utils/google-calendar-api.ts +115 -0
- package/src/electron/utils/google-workspace-api.ts +228 -0
- package/src/electron/utils/google-workspace-auth.ts +109 -0
- package/src/electron/utils/google-workspace-oauth.ts +232 -0
- package/src/electron/utils/notion-api.ts +126 -0
- package/src/electron/utils/onedrive-api.ts +137 -0
- package/src/electron/utils/sharepoint-api.ts +132 -0
- package/src/electron/utils/validation.ts +128 -1
- package/src/electron/utils/x-cli.ts +1 -1
- package/src/renderer/App.tsx +119 -8
- package/src/renderer/components/ActivityFeedItem.tsx +34 -17
- package/src/renderer/components/AgentWorkingStatePanel.tsx +7 -5
- package/src/renderer/components/AppearanceSettings.tsx +37 -2
- package/src/renderer/components/BlueBubblesSettings.tsx +18 -7
- package/src/renderer/components/BoxSettings.tsx +203 -0
- package/src/renderer/components/BrowserView.tsx +101 -0
- package/src/renderer/components/BuiltinToolsSettings.tsx +105 -0
- package/src/renderer/components/CanvasPreview.tsx +68 -1
- package/src/renderer/components/ConnectorEnvModal.tsx +116 -0
- package/src/renderer/components/ConnectorSetupModal.tsx +566 -0
- package/src/renderer/components/ConnectorsSettings.tsx +397 -0
- package/src/renderer/components/ControlPlaneSettings.tsx +2 -0
- package/src/renderer/components/DiscordSettings.tsx +18 -7
- package/src/renderer/components/DropboxSettings.tsx +202 -0
- package/src/renderer/components/EmailSettings.tsx +18 -7
- package/src/renderer/components/FileViewer.tsx +21 -13
- package/src/renderer/components/GoogleChatSettings.tsx +17 -7
- package/src/renderer/components/GoogleWorkspaceSettings.tsx +332 -0
- package/src/renderer/components/ImessageSettings.tsx +22 -11
- package/src/renderer/components/LineIcons.tsx +376 -0
- package/src/renderer/components/LineSettings.tsx +18 -7
- package/src/renderer/components/MCPSettings.tsx +56 -0
- package/src/renderer/components/MainContent.tsx +740 -76
- package/src/renderer/components/MatrixSettings.tsx +18 -7
- package/src/renderer/components/MattermostSettings.tsx +18 -7
- package/src/renderer/components/NodesSettings.tsx +58 -99
- package/src/renderer/components/NotificationPanel.tsx +25 -11
- package/src/renderer/components/NotionSettings.tsx +231 -0
- package/src/renderer/components/Onboarding/Onboarding.tsx +13 -1
- package/src/renderer/components/OnboardingModal.tsx +70 -1
- package/src/renderer/components/OneDriveSettings.tsx +212 -0
- package/src/renderer/components/RightPanel.tsx +141 -28
- package/src/renderer/components/ScheduledTasksSettings.tsx +10 -62
- package/src/renderer/components/SearchSettings.tsx +118 -114
- package/src/renderer/components/Settings.tsx +1425 -651
- package/src/renderer/components/SharePointSettings.tsx +224 -0
- package/src/renderer/components/Sidebar.tsx +94 -19
- package/src/renderer/components/SignalSettings.tsx +18 -7
- package/src/renderer/components/SkillHubBrowser.tsx +144 -185
- package/src/renderer/components/SlackSettings.tsx +18 -7
- package/src/renderer/components/TaskQuickActions.tsx +11 -6
- package/src/renderer/components/TaskTimeline.tsx +58 -26
- package/src/renderer/components/TeamsSettings.tsx +18 -7
- package/src/renderer/components/TelegramSettings.tsx +18 -7
- package/src/renderer/components/ThemeIcon.tsx +16 -0
- package/src/renderer/components/TwitchSettings.tsx +18 -7
- package/src/renderer/components/VoiceSettings.tsx +30 -74
- package/src/renderer/components/WhatsAppSettings.tsx +48 -37
- package/src/renderer/components/WorkingStateHistory.tsx +7 -5
- package/src/renderer/components/WorkspaceSelector.tsx +42 -13
- package/src/renderer/hooks/useOnboardingFlow.ts +21 -0
- package/src/renderer/styles/index.css +2333 -209
- package/src/shared/channelMessages.ts +367 -4
- package/src/shared/llm-provider-catalog.ts +217 -0
- package/src/shared/types.ts +251 -2
|
@@ -29,6 +29,8 @@ export class BedrockProvider implements LLMProvider {
|
|
|
29
29
|
private client: BedrockRuntimeClient;
|
|
30
30
|
private model: string;
|
|
31
31
|
|
|
32
|
+
private static readonly toolNameRegex = /^[a-zA-Z0-9_-]+$/;
|
|
33
|
+
|
|
32
34
|
constructor(config: LLMProviderConfig) {
|
|
33
35
|
const clientConfig: BedrockRuntimeClientConfig = {
|
|
34
36
|
region: config.awsRegion || 'us-east-1',
|
|
@@ -56,9 +58,10 @@ export class BedrockProvider implements LLMProvider {
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
async createMessage(request: LLMRequest): Promise<LLMResponse> {
|
|
59
|
-
const
|
|
61
|
+
const toolNameMap = request.tools ? this.buildToolNameMap(request.tools) : undefined;
|
|
62
|
+
const messages = this.convertMessages(request.messages, toolNameMap);
|
|
60
63
|
const system = this.convertSystem(request.system);
|
|
61
|
-
const toolConfig = request.tools ? this.convertTools(request.tools) : undefined;
|
|
64
|
+
const toolConfig = request.tools ? this.convertTools(request.tools, toolNameMap) : undefined;
|
|
62
65
|
|
|
63
66
|
const command = new ConverseCommand({
|
|
64
67
|
modelId: request.model,
|
|
@@ -77,7 +80,7 @@ export class BedrockProvider implements LLMProvider {
|
|
|
77
80
|
// Pass abort signal to allow cancellation
|
|
78
81
|
request.signal ? { abortSignal: request.signal } : undefined
|
|
79
82
|
);
|
|
80
|
-
return this.convertResponse(response);
|
|
83
|
+
return this.convertResponse(response, toolNameMap);
|
|
81
84
|
} catch (error: any) {
|
|
82
85
|
// Handle abort errors gracefully
|
|
83
86
|
if (error.name === 'AbortError' || error.message?.includes('aborted')) {
|
|
@@ -135,7 +138,7 @@ export class BedrockProvider implements LLMProvider {
|
|
|
135
138
|
return [{ text: system }];
|
|
136
139
|
}
|
|
137
140
|
|
|
138
|
-
private convertMessages(messages: LLMMessage[]): Message[] {
|
|
141
|
+
private convertMessages(messages: LLMMessage[], toolNameMap?: ToolNameMap): Message[] {
|
|
139
142
|
return messages.map((msg) => {
|
|
140
143
|
const content: ContentBlock[] = [];
|
|
141
144
|
|
|
@@ -146,10 +149,11 @@ export class BedrockProvider implements LLMProvider {
|
|
|
146
149
|
if (item.type === 'text') {
|
|
147
150
|
content.push({ text: item.text });
|
|
148
151
|
} else if (item.type === 'tool_use') {
|
|
152
|
+
const mappedName = toolNameMap?.toProvider.get(item.name) || item.name;
|
|
149
153
|
content.push({
|
|
150
154
|
toolUse: {
|
|
151
155
|
toolUseId: item.id,
|
|
152
|
-
name:
|
|
156
|
+
name: mappedName,
|
|
153
157
|
input: item.input,
|
|
154
158
|
},
|
|
155
159
|
});
|
|
@@ -172,11 +176,11 @@ export class BedrockProvider implements LLMProvider {
|
|
|
172
176
|
});
|
|
173
177
|
}
|
|
174
178
|
|
|
175
|
-
private convertTools(tools: LLMTool[]): ToolConfiguration {
|
|
179
|
+
private convertTools(tools: LLMTool[], toolNameMap?: ToolNameMap): ToolConfiguration {
|
|
176
180
|
return {
|
|
177
181
|
tools: tools.map((tool) => ({
|
|
178
182
|
toolSpec: {
|
|
179
|
-
name: tool.name,
|
|
183
|
+
name: toolNameMap?.toProvider.get(tool.name) || tool.name,
|
|
180
184
|
description: tool.description,
|
|
181
185
|
inputSchema: {
|
|
182
186
|
json: tool.input_schema,
|
|
@@ -186,7 +190,7 @@ export class BedrockProvider implements LLMProvider {
|
|
|
186
190
|
};
|
|
187
191
|
}
|
|
188
192
|
|
|
189
|
-
private convertResponse(response: any): LLMResponse {
|
|
193
|
+
private convertResponse(response: any, toolNameMap?: ToolNameMap): LLMResponse {
|
|
190
194
|
const content: LLMContent[] = [];
|
|
191
195
|
|
|
192
196
|
if (response.output?.message?.content) {
|
|
@@ -197,10 +201,11 @@ export class BedrockProvider implements LLMProvider {
|
|
|
197
201
|
text: block.text,
|
|
198
202
|
});
|
|
199
203
|
} else if (block.toolUse) {
|
|
204
|
+
const mappedName = toolNameMap?.fromProvider.get(block.toolUse.name) || block.toolUse.name;
|
|
200
205
|
content.push({
|
|
201
206
|
type: 'tool_use',
|
|
202
207
|
id: block.toolUse.toolUseId,
|
|
203
|
-
name:
|
|
208
|
+
name: mappedName,
|
|
204
209
|
input: block.toolUse.input,
|
|
205
210
|
});
|
|
206
211
|
}
|
|
@@ -219,6 +224,49 @@ export class BedrockProvider implements LLMProvider {
|
|
|
219
224
|
};
|
|
220
225
|
}
|
|
221
226
|
|
|
227
|
+
private buildToolNameMap(tools: LLMTool[]): ToolNameMap {
|
|
228
|
+
const toProvider = new Map<string, string>();
|
|
229
|
+
const fromProvider = new Map<string, string>();
|
|
230
|
+
const used = new Set<string>();
|
|
231
|
+
|
|
232
|
+
for (const tool of tools) {
|
|
233
|
+
let base = this.normalizeToolName(tool.name);
|
|
234
|
+
if (!base) {
|
|
235
|
+
base = `tool_${this.shortHash(tool.name)}`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let candidate = base;
|
|
239
|
+
if (used.has(candidate)) {
|
|
240
|
+
const hashed = `${base}_${this.shortHash(tool.name)}`;
|
|
241
|
+
candidate = hashed;
|
|
242
|
+
let counter = 1;
|
|
243
|
+
while (used.has(candidate)) {
|
|
244
|
+
candidate = `${hashed}_${counter++}`;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
used.add(candidate);
|
|
249
|
+
toProvider.set(tool.name, candidate);
|
|
250
|
+
fromProvider.set(candidate, tool.name);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return { toProvider, fromProvider };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private normalizeToolName(name: string): string {
|
|
257
|
+
const sanitized = name.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
258
|
+
return BedrockProvider.toolNameRegex.test(sanitized) ? sanitized : '';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private shortHash(input: string): string {
|
|
262
|
+
let hash = 2166136261;
|
|
263
|
+
for (let i = 0; i < input.length; i++) {
|
|
264
|
+
hash ^= input.charCodeAt(i);
|
|
265
|
+
hash = Math.imul(hash, 16777619);
|
|
266
|
+
}
|
|
267
|
+
return (hash >>> 0).toString(36);
|
|
268
|
+
}
|
|
269
|
+
|
|
222
270
|
private mapStopReason(reason: StopReason | undefined): LLMResponse['stopReason'] {
|
|
223
271
|
switch (reason) {
|
|
224
272
|
case 'end_turn':
|
|
@@ -234,3 +282,8 @@ export class BedrockProvider implements LLMProvider {
|
|
|
234
282
|
}
|
|
235
283
|
}
|
|
236
284
|
}
|
|
285
|
+
|
|
286
|
+
interface ToolNameMap {
|
|
287
|
+
toProvider: Map<string, string>;
|
|
288
|
+
fromProvider: Map<string, string>;
|
|
289
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { LLMProvider, LLMProviderConfig, LLMRequest, LLMResponse } from './types';
|
|
2
|
+
import { OpenAICompatibleProvider } from './openai-compatible-provider';
|
|
3
|
+
|
|
4
|
+
const COPILOT_TOKEN_URL = 'https://api.github.com/copilot_internal/v2/token';
|
|
5
|
+
const DEFAULT_COPILOT_BASE_URL = 'https://api.individual.githubcopilot.com';
|
|
6
|
+
|
|
7
|
+
type CopilotTokenCache = {
|
|
8
|
+
token: string;
|
|
9
|
+
expiresAt: number;
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function isTokenValid(cache: CopilotTokenCache, now = Date.now()): boolean {
|
|
14
|
+
return cache.expiresAt - now > 5 * 60 * 1000;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function parseCopilotTokenResponse(payload: any): { token: string; expiresAt: number } {
|
|
18
|
+
if (!payload || typeof payload !== 'object') {
|
|
19
|
+
throw new Error('Unexpected response from Copilot token endpoint');
|
|
20
|
+
}
|
|
21
|
+
const token = payload.token;
|
|
22
|
+
const expiresAt = payload.expires_at;
|
|
23
|
+
if (typeof token !== 'string' || !token.trim()) {
|
|
24
|
+
throw new Error('Copilot token response missing token');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (typeof expiresAt === 'number' && Number.isFinite(expiresAt)) {
|
|
28
|
+
return { token, expiresAt: expiresAt > 10_000_000_000 ? expiresAt : expiresAt * 1000 };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof expiresAt === 'string' && expiresAt.trim()) {
|
|
32
|
+
const parsed = Number.parseInt(expiresAt, 10);
|
|
33
|
+
if (!Number.isFinite(parsed)) {
|
|
34
|
+
throw new Error('Copilot token response has invalid expires_at');
|
|
35
|
+
}
|
|
36
|
+
return { token, expiresAt: parsed > 10_000_000_000 ? parsed : parsed * 1000 };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
throw new Error('Copilot token response missing expires_at');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function deriveCopilotBaseUrl(token: string): string {
|
|
43
|
+
const match = token.match(/(?:^|;)\s*proxy-ep=([^;\s]+)/i);
|
|
44
|
+
const proxyEp = match?.[1]?.trim();
|
|
45
|
+
if (!proxyEp) return DEFAULT_COPILOT_BASE_URL;
|
|
46
|
+
const host = proxyEp.replace(/^https?:\/\//, '').replace(/^proxy\./i, 'api.');
|
|
47
|
+
return host ? `https://${host}` : DEFAULT_COPILOT_BASE_URL;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class GitHubCopilotProvider implements LLMProvider {
|
|
51
|
+
readonly type = 'github-copilot' as const;
|
|
52
|
+
private githubToken: string;
|
|
53
|
+
private model: string;
|
|
54
|
+
private static cache?: CopilotTokenCache;
|
|
55
|
+
|
|
56
|
+
constructor(config: LLMProviderConfig) {
|
|
57
|
+
const token = config.providerApiKey;
|
|
58
|
+
if (!token) {
|
|
59
|
+
throw new Error('GitHub token is required for Copilot. Configure it in Settings.');
|
|
60
|
+
}
|
|
61
|
+
this.githubToken = token;
|
|
62
|
+
this.model = config.model || 'gpt-4o';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async createMessage(request: LLMRequest): Promise<LLMResponse> {
|
|
66
|
+
const client = await this.getClient(request.model || this.model);
|
|
67
|
+
return client.createMessage(request);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async testConnection(): Promise<{ success: boolean; error?: string }> {
|
|
71
|
+
try {
|
|
72
|
+
const client = await this.getClient(this.model);
|
|
73
|
+
return await client.testConnection();
|
|
74
|
+
} catch (error: any) {
|
|
75
|
+
return { success: false, error: error.message || 'Failed to connect to Copilot' };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private async getClient(model: string): Promise<OpenAICompatibleProvider> {
|
|
80
|
+
const auth = await this.getCopilotAuth();
|
|
81
|
+
return new OpenAICompatibleProvider({
|
|
82
|
+
type: 'github-copilot',
|
|
83
|
+
providerName: 'GitHub Copilot',
|
|
84
|
+
apiKey: auth.token,
|
|
85
|
+
baseUrl: auth.baseUrl,
|
|
86
|
+
defaultModel: model,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private async getCopilotAuth(): Promise<CopilotTokenCache> {
|
|
91
|
+
if (GitHubCopilotProvider.cache && isTokenValid(GitHubCopilotProvider.cache)) {
|
|
92
|
+
return GitHubCopilotProvider.cache;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const response = await fetch(COPILOT_TOKEN_URL, {
|
|
96
|
+
method: 'GET',
|
|
97
|
+
headers: {
|
|
98
|
+
Accept: 'application/json',
|
|
99
|
+
Authorization: `Bearer ${this.githubToken}`,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (!response.ok) {
|
|
104
|
+
throw new Error(`Copilot token exchange failed: HTTP ${response.status}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const json = await response.json();
|
|
108
|
+
const parsed = parseCopilotTokenResponse(json);
|
|
109
|
+
const cache: CopilotTokenCache = {
|
|
110
|
+
token: parsed.token,
|
|
111
|
+
expiresAt: parsed.expiresAt,
|
|
112
|
+
baseUrl: deriveCopilotBaseUrl(parsed.token),
|
|
113
|
+
};
|
|
114
|
+
GitHubCopilotProvider.cache = cache;
|
|
115
|
+
return cache;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { LLMProvider, LLMProviderConfig, LLMRequest, LLMResponse } from './types';
|
|
2
|
+
import { OpenAICompatibleProvider } from './openai-compatible-provider';
|
|
3
|
+
|
|
4
|
+
const GROQ_BASE_URL = 'https://api.groq.com/openai/v1';
|
|
5
|
+
const DEFAULT_GROQ_MODEL = 'llama-3.1-8b-instant';
|
|
6
|
+
|
|
7
|
+
export class GroqProvider implements LLMProvider {
|
|
8
|
+
readonly type = 'groq' as const;
|
|
9
|
+
private client: OpenAICompatibleProvider;
|
|
10
|
+
|
|
11
|
+
constructor(config: LLMProviderConfig) {
|
|
12
|
+
const apiKey = config.groqApiKey;
|
|
13
|
+
if (!apiKey) {
|
|
14
|
+
throw new Error('Groq API key is required. Configure it in Settings.');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const baseUrl = config.groqBaseUrl || GROQ_BASE_URL;
|
|
18
|
+
|
|
19
|
+
this.client = new OpenAICompatibleProvider({
|
|
20
|
+
type: 'groq',
|
|
21
|
+
providerName: 'Groq',
|
|
22
|
+
apiKey,
|
|
23
|
+
baseUrl,
|
|
24
|
+
defaultModel: config.model || DEFAULT_GROQ_MODEL,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
createMessage(request: LLMRequest): Promise<LLMResponse> {
|
|
29
|
+
return this.client.createMessage(request);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
testConnection() {
|
|
33
|
+
return this.client.testConnection();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getAvailableModels() {
|
|
37
|
+
return this.client.getAvailableModels();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -5,5 +5,11 @@ export { OllamaProvider } from './ollama-provider';
|
|
|
5
5
|
export { GeminiProvider } from './gemini-provider';
|
|
6
6
|
export { OpenRouterProvider } from './openrouter-provider';
|
|
7
7
|
export { OpenAIProvider } from './openai-provider';
|
|
8
|
+
export { AzureOpenAIProvider } from './azure-openai-provider';
|
|
9
|
+
export { GroqProvider } from './groq-provider';
|
|
10
|
+
export { XAIProvider } from './xai-provider';
|
|
11
|
+
export { KimiProvider } from './kimi-provider';
|
|
12
|
+
export { AnthropicCompatibleProvider } from './anthropic-compatible-provider';
|
|
13
|
+
export { GitHubCopilotProvider } from './github-copilot-provider';
|
|
8
14
|
export { OpenAIOAuth, OpenAIOAuthTokens } from './openai-oauth';
|
|
9
15
|
export { LLMProviderFactory, LLMSettings, CachedModelInfo } from './provider-factory';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { LLMProvider, LLMProviderConfig, LLMRequest, LLMResponse } from './types';
|
|
2
|
+
import { OpenAICompatibleProvider } from './openai-compatible-provider';
|
|
3
|
+
|
|
4
|
+
const KIMI_BASE_URL = 'https://api.moonshot.ai/v1';
|
|
5
|
+
const DEFAULT_KIMI_MODEL = 'kimi-k2.5';
|
|
6
|
+
|
|
7
|
+
export class KimiProvider implements LLMProvider {
|
|
8
|
+
readonly type = 'kimi' as const;
|
|
9
|
+
private client: OpenAICompatibleProvider;
|
|
10
|
+
|
|
11
|
+
constructor(config: LLMProviderConfig) {
|
|
12
|
+
const apiKey = config.kimiApiKey;
|
|
13
|
+
if (!apiKey) {
|
|
14
|
+
throw new Error('Kimi API key is required. Configure it in Settings.');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const baseUrl = config.kimiBaseUrl || KIMI_BASE_URL;
|
|
18
|
+
|
|
19
|
+
this.client = new OpenAICompatibleProvider({
|
|
20
|
+
type: 'kimi',
|
|
21
|
+
providerName: 'Kimi',
|
|
22
|
+
apiKey,
|
|
23
|
+
baseUrl,
|
|
24
|
+
defaultModel: config.model || DEFAULT_KIMI_MODEL,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
createMessage(request: LLMRequest): Promise<LLMResponse> {
|
|
29
|
+
return this.client.createMessage(request);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
testConnection() {
|
|
33
|
+
return this.client.testConnection();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getAvailableModels() {
|
|
37
|
+
return this.client.getAvailableModels();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LLMProvider,
|
|
3
|
+
LLMProviderType,
|
|
4
|
+
LLMRequest,
|
|
5
|
+
LLMResponse,
|
|
6
|
+
} from './types';
|
|
7
|
+
import {
|
|
8
|
+
toOpenAICompatibleMessages,
|
|
9
|
+
toOpenAICompatibleTools,
|
|
10
|
+
fromOpenAICompatibleResponse,
|
|
11
|
+
} from './openai-compatible';
|
|
12
|
+
|
|
13
|
+
export interface OpenAICompatibleProviderOptions {
|
|
14
|
+
type: LLMProviderType;
|
|
15
|
+
providerName: string;
|
|
16
|
+
apiKey: string;
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
defaultModel: string;
|
|
19
|
+
extraHeaders?: Record<string, string>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class OpenAICompatibleProvider implements LLMProvider {
|
|
23
|
+
readonly type: LLMProviderType;
|
|
24
|
+
private apiKey: string;
|
|
25
|
+
private baseUrl: string;
|
|
26
|
+
private defaultModel: string;
|
|
27
|
+
private providerName: string;
|
|
28
|
+
private extraHeaders?: Record<string, string>;
|
|
29
|
+
|
|
30
|
+
constructor(options: OpenAICompatibleProviderOptions) {
|
|
31
|
+
this.type = options.type;
|
|
32
|
+
this.apiKey = options.apiKey;
|
|
33
|
+
this.baseUrl = options.baseUrl;
|
|
34
|
+
this.defaultModel = options.defaultModel;
|
|
35
|
+
this.providerName = options.providerName;
|
|
36
|
+
this.extraHeaders = options.extraHeaders;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async createMessage(request: LLMRequest): Promise<LLMResponse> {
|
|
40
|
+
const messages = toOpenAICompatibleMessages(request.messages, request.system);
|
|
41
|
+
const tools = request.tools ? toOpenAICompatibleTools(request.tools) : undefined;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const model = request.model || this.defaultModel;
|
|
45
|
+
console.log(`[${this.providerName}] Calling API with model: ${model}`);
|
|
46
|
+
|
|
47
|
+
const headers: Record<string, string> = {
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
...(this.extraHeaders || {}),
|
|
50
|
+
};
|
|
51
|
+
if (this.apiKey) {
|
|
52
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
headers,
|
|
58
|
+
body: JSON.stringify({
|
|
59
|
+
model,
|
|
60
|
+
messages,
|
|
61
|
+
max_tokens: request.maxTokens,
|
|
62
|
+
...(tools && { tools, tool_choice: 'auto' }),
|
|
63
|
+
}),
|
|
64
|
+
signal: request.signal,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!response.ok) {
|
|
68
|
+
const errorData = await response.json().catch(() => ({})) as { error?: { message?: string } };
|
|
69
|
+
throw new Error(
|
|
70
|
+
`${this.providerName} API error: ${response.status} ${response.statusText}` +
|
|
71
|
+
(errorData.error?.message ? ` - ${errorData.error.message}` : '')
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const data = await response.json() as any;
|
|
76
|
+
return fromOpenAICompatibleResponse(data);
|
|
77
|
+
} catch (error: any) {
|
|
78
|
+
if (error.name === 'AbortError' || error.message?.includes('aborted')) {
|
|
79
|
+
console.log(`[${this.providerName}] Request aborted`);
|
|
80
|
+
throw new Error('Request cancelled');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.error(`[${this.providerName}] API error:`, {
|
|
84
|
+
message: error.message,
|
|
85
|
+
status: error.status,
|
|
86
|
+
});
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async testConnection(): Promise<{ success: boolean; error?: string }> {
|
|
92
|
+
try {
|
|
93
|
+
const headers: Record<string, string> = {
|
|
94
|
+
'Content-Type': 'application/json',
|
|
95
|
+
...(this.extraHeaders || {}),
|
|
96
|
+
};
|
|
97
|
+
if (this.apiKey) {
|
|
98
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
102
|
+
method: 'POST',
|
|
103
|
+
headers,
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
model: this.defaultModel,
|
|
106
|
+
messages: [{ role: 'user', content: 'Hi' }],
|
|
107
|
+
max_tokens: 10,
|
|
108
|
+
}),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (!response.ok) {
|
|
112
|
+
const errorData = await response.json().catch(() => ({})) as { error?: { message?: string } };
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
error: errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { success: true };
|
|
120
|
+
} catch (error: any) {
|
|
121
|
+
return {
|
|
122
|
+
success: false,
|
|
123
|
+
error: error.message || `Failed to connect to ${this.providerName} API`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async getAvailableModels(): Promise<Array<{ id: string; name: string }>> {
|
|
129
|
+
try {
|
|
130
|
+
const headers: Record<string, string> = {};
|
|
131
|
+
if (this.apiKey) {
|
|
132
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const response = await fetch(`${this.baseUrl}/models`, {
|
|
136
|
+
headers,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const data = await response.json() as { data?: any[] };
|
|
144
|
+
return (data.data || []).map((model: any) => ({
|
|
145
|
+
id: model.id,
|
|
146
|
+
name: model.id,
|
|
147
|
+
}));
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error(`[${this.providerName}] Failed to fetch models:`, error);
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LLMContent,
|
|
3
|
+
LLMMessage,
|
|
4
|
+
LLMResponse,
|
|
5
|
+
LLMTool,
|
|
6
|
+
} from './types';
|
|
7
|
+
|
|
8
|
+
export function toOpenAICompatibleMessages(
|
|
9
|
+
messages: LLMMessage[],
|
|
10
|
+
system?: string
|
|
11
|
+
): Array<{ role: string; content: any; tool_call_id?: string; tool_calls?: any[] }> {
|
|
12
|
+
const result: Array<{ role: string; content: any; tool_call_id?: string; tool_calls?: any[] }> = [];
|
|
13
|
+
|
|
14
|
+
if (system) {
|
|
15
|
+
result.push({ role: 'system', content: system });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
for (const msg of messages) {
|
|
19
|
+
if (typeof msg.content === 'string') {
|
|
20
|
+
result.push({ role: msg.role, content: msg.content });
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
for (const item of msg.content) {
|
|
25
|
+
if (item.type === 'tool_result') {
|
|
26
|
+
result.push({
|
|
27
|
+
role: 'tool',
|
|
28
|
+
content: item.content,
|
|
29
|
+
tool_call_id: item.tool_use_id,
|
|
30
|
+
});
|
|
31
|
+
} else if (item.type === 'tool_use') {
|
|
32
|
+
result.push({
|
|
33
|
+
role: 'assistant',
|
|
34
|
+
content: null,
|
|
35
|
+
tool_calls: [{
|
|
36
|
+
id: item.id,
|
|
37
|
+
type: 'function',
|
|
38
|
+
function: {
|
|
39
|
+
name: item.name,
|
|
40
|
+
arguments: JSON.stringify(item.input),
|
|
41
|
+
},
|
|
42
|
+
}],
|
|
43
|
+
});
|
|
44
|
+
} else if (item.type === 'text') {
|
|
45
|
+
result.push({ role: msg.role, content: item.text });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function toOpenAICompatibleTools(tools: LLMTool[]): Array<{
|
|
54
|
+
type: 'function';
|
|
55
|
+
function: {
|
|
56
|
+
name: string;
|
|
57
|
+
description: string;
|
|
58
|
+
parameters: any;
|
|
59
|
+
};
|
|
60
|
+
}> {
|
|
61
|
+
return tools.map((tool) => ({
|
|
62
|
+
type: 'function' as const,
|
|
63
|
+
function: {
|
|
64
|
+
name: tool.name,
|
|
65
|
+
description: tool.description,
|
|
66
|
+
parameters: tool.input_schema,
|
|
67
|
+
},
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function fromOpenAICompatibleResponse(response: any): LLMResponse {
|
|
72
|
+
const content: LLMContent[] = [];
|
|
73
|
+
const choice = response.choices?.[0];
|
|
74
|
+
|
|
75
|
+
if (!choice) {
|
|
76
|
+
return {
|
|
77
|
+
content: [{ type: 'text', text: '' }],
|
|
78
|
+
stopReason: 'end_turn',
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const message = choice.message;
|
|
83
|
+
|
|
84
|
+
if (message?.content) {
|
|
85
|
+
content.push({
|
|
86
|
+
type: 'text',
|
|
87
|
+
text: message.content,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (message?.tool_calls) {
|
|
92
|
+
for (const toolCall of message.tool_calls) {
|
|
93
|
+
if (toolCall.type === 'function') {
|
|
94
|
+
content.push({
|
|
95
|
+
type: 'tool_use',
|
|
96
|
+
id: toolCall.id,
|
|
97
|
+
name: toolCall.function.name,
|
|
98
|
+
input: JSON.parse(toolCall.function.arguments || '{}'),
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (content.length === 0) {
|
|
105
|
+
content.push({ type: 'text', text: '' });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
content,
|
|
110
|
+
stopReason: mapStopReason(choice.finish_reason),
|
|
111
|
+
usage: response.usage
|
|
112
|
+
? {
|
|
113
|
+
inputTokens: response.usage.prompt_tokens || 0,
|
|
114
|
+
outputTokens: response.usage.completion_tokens || 0,
|
|
115
|
+
}
|
|
116
|
+
: undefined,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function mapStopReason(finishReason?: string): LLMResponse['stopReason'] {
|
|
121
|
+
switch (finishReason) {
|
|
122
|
+
case 'stop':
|
|
123
|
+
return 'end_turn';
|
|
124
|
+
case 'length':
|
|
125
|
+
return 'max_tokens';
|
|
126
|
+
case 'tool_calls':
|
|
127
|
+
return 'tool_use';
|
|
128
|
+
case 'content_filter':
|
|
129
|
+
return 'stop_sequence';
|
|
130
|
+
default:
|
|
131
|
+
return 'end_turn';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -20,11 +20,12 @@ export interface OpenAIOAuthTokens {
|
|
|
20
20
|
* Convert pi-ai OAuthCredentials to our token format
|
|
21
21
|
*/
|
|
22
22
|
function credentialsToTokens(credentials: OAuthCredentials): OpenAIOAuthTokens {
|
|
23
|
+
const email = typeof credentials.email === 'string' ? credentials.email : undefined;
|
|
23
24
|
return {
|
|
24
25
|
access_token: credentials.access,
|
|
25
26
|
refresh_token: credentials.refresh,
|
|
26
27
|
expires_at: credentials.expires,
|
|
27
|
-
email
|
|
28
|
+
email,
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
export class OpenRouterProvider implements LLMProvider {
|
|
16
16
|
readonly type = 'openrouter' as const;
|
|
17
17
|
private apiKey: string;
|
|
18
|
-
private baseUrl
|
|
18
|
+
private baseUrl: string;
|
|
19
19
|
private defaultModel: string;
|
|
20
20
|
|
|
21
21
|
constructor(config: LLMProviderConfig) {
|
|
@@ -25,6 +25,7 @@ export class OpenRouterProvider implements LLMProvider {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
this.apiKey = apiKey;
|
|
28
|
+
this.baseUrl = config.openrouterBaseUrl || 'https://openrouter.ai/api/v1';
|
|
28
29
|
this.defaultModel = config.model || 'anthropic/claude-3.5-sonnet';
|
|
29
30
|
}
|
|
30
31
|
|