@things-factory/ai-client-base 10.1.149 → 10.1.155

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.
Files changed (76) hide show
  1. package/config/config.development.js +2 -68
  2. package/config/config.production.js +2 -20
  3. package/dist-server/service/agent/agent-directory.d.ts +71 -0
  4. package/dist-server/service/agent/agent-directory.js +129 -0
  5. package/dist-server/service/agent/agent-directory.js.map +1 -0
  6. package/dist-server/service/agent/agent-settings-save.d.ts +38 -0
  7. package/dist-server/service/agent/agent-settings-save.js +153 -0
  8. package/dist-server/service/agent/agent-settings-save.js.map +1 -0
  9. package/dist-server/service/agent/agent-spec.d.ts +43 -0
  10. package/dist-server/service/agent/agent-spec.js +83 -0
  11. package/dist-server/service/agent/agent-spec.js.map +1 -0
  12. package/dist-server/service/agent/ai-client-for.d.ts +53 -0
  13. package/dist-server/service/agent/ai-client-for.js +166 -0
  14. package/dist-server/service/agent/ai-client-for.js.map +1 -0
  15. package/dist-server/service/agent/ai-usage-summary.d.ts +37 -0
  16. package/dist-server/service/agent/ai-usage-summary.js +108 -0
  17. package/dist-server/service/agent/ai-usage-summary.js.map +1 -0
  18. package/dist-server/service/agent/ai-usage.d.ts +35 -0
  19. package/dist-server/service/agent/ai-usage.js +118 -0
  20. package/dist-server/service/agent/ai-usage.js.map +1 -0
  21. package/dist-server/service/agent/index.d.ts +7 -0
  22. package/dist-server/service/agent/index.js +13 -0
  23. package/dist-server/service/agent/index.js.map +1 -0
  24. package/dist-server/service/agent/provider-models.d.ts +12 -0
  25. package/dist-server/service/agent/provider-models.js +37 -0
  26. package/dist-server/service/agent/provider-models.js.map +1 -0
  27. package/dist-server/service/capability.d.ts +21 -10
  28. package/dist-server/service/capability.js +20 -49
  29. package/dist-server/service/capability.js.map +1 -1
  30. package/dist-server/service/factory.d.ts +7 -12
  31. package/dist-server/service/factory.js +0 -45
  32. package/dist-server/service/factory.js.map +1 -1
  33. package/dist-server/service/index.d.ts +3 -0
  34. package/dist-server/service/index.js +6 -1
  35. package/dist-server/service/index.js.map +1 -1
  36. package/dist-server/service/providers/anthropic.d.ts +2 -0
  37. package/dist-server/service/providers/anthropic.js +16 -0
  38. package/dist-server/service/providers/anthropic.js.map +1 -1
  39. package/dist-server/service/providers/gemini.d.ts +13 -0
  40. package/dist-server/service/providers/gemini.js +52 -12
  41. package/dist-server/service/providers/gemini.js.map +1 -1
  42. package/dist-server/service/providers/openai.d.ts +6 -0
  43. package/dist-server/service/providers/openai.js +34 -8
  44. package/dist-server/service/providers/openai.js.map +1 -1
  45. package/dist-server/service/types.d.ts +16 -2
  46. package/dist-server/service/types.js.map +1 -1
  47. package/dist-server/tsconfig.tsbuildinfo +1 -1
  48. package/package.json +5 -2
  49. package/server/service/agent/agent-directory.test.ts +206 -0
  50. package/server/service/agent/agent-directory.ts +198 -0
  51. package/server/service/agent/agent-settings-save.test.ts +151 -0
  52. package/server/service/agent/agent-settings-save.ts +205 -0
  53. package/server/service/agent/agent-spec.ts +103 -0
  54. package/server/service/agent/ai-client-for.test.ts +141 -0
  55. package/server/service/agent/ai-client-for.ts +201 -0
  56. package/server/service/agent/ai-usage-summary.test.ts +65 -0
  57. package/server/service/agent/ai-usage-summary.ts +146 -0
  58. package/server/service/agent/ai-usage.ts +97 -0
  59. package/server/service/agent/index.ts +7 -0
  60. package/server/service/agent/one-door.test.ts +57 -0
  61. package/server/service/agent/provider-models.ts +43 -0
  62. package/server/service/capability.test.ts +26 -55
  63. package/server/service/capability.ts +25 -42
  64. package/server/service/factory.ts +4 -44
  65. package/server/service/index.ts +6 -1
  66. package/server/service/providers/anthropic.ts +17 -0
  67. package/server/service/providers/gemini.ts +54 -12
  68. package/server/service/providers/openai-models.test.ts +30 -0
  69. package/server/service/providers/openai.ts +34 -8
  70. package/server/service/types.ts +18 -2
  71. package/translations/en.json +13 -0
  72. package/translations/es.json +13 -0
  73. package/translations/ja.json +13 -0
  74. package/translations/ko.json +13 -0
  75. package/translations/ms.json +13 -0
  76. package/translations/zh.json +13 -0
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../server/service/providers/anthropic.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Anthropic (Claude) AI Client adapter.\n *\n * 기본 모델: claude-sonnet-4-6 (구성에서 override 가능).\n * SDK: @anthropic-ai/sdk\n */\nimport Anthropic from '@anthropic-ai/sdk'\nimport type {\n AIChatOptions,\n AIClient,\n AIClientConfig,\n AIContentPart,\n AIJSONOptions,\n AIMessage,\n AITool,\n AIToolCall,\n AIToolChatOptions,\n AIToolChatResult,\n AIVisionOptions\n} from '../types.js'\nimport { registerAIProvider } from '../factory.js'\nimport { parseJSONResponse } from '../utils/parse-json.js'\n\nconst DEFAULT_MODEL = 'claude-sonnet-4-6'\nconst DEFAULT_MAX_TOKENS = 4096\n\nexport class AnthropicAIClient implements AIClient {\n readonly id: string\n private sdk: Anthropic\n private defaultModel: string\n\n constructor(config: AIClientConfig) {\n this.sdk = new Anthropic({\n apiKey: config.apiKey,\n ...(config.baseURL && { baseURL: config.baseURL })\n })\n this.defaultModel = config.model ?? DEFAULT_MODEL\n this.id = `anthropic:${this.defaultModel}`\n }\n\n async chat(messages: AIMessage[], options: AIChatOptions = {}): Promise<string> {\n const systemFromMessages = messages\n .filter(m => m.role === 'system')\n .map(extractTextContent)\n .filter(Boolean)\n .join('\\n\\n')\n const system = [options.systemPrompt, systemFromMessages].filter(Boolean).join('\\n\\n')\n\n const conversation = messages\n .filter(m => m.role !== 'system')\n .map(toAnthropicMessage)\n\n const response = await this.sdk.messages.create({\n model: options.model ?? this.defaultModel,\n max_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(system && { system }),\n ...(options.stopSequences && { stop_sequences: options.stopSequences }),\n messages: conversation\n } as any)\n\n return extractResponseText(response)\n }\n\n async generateJSON<T = unknown>(\n prompt: string | AIMessage[],\n options: AIJSONOptions = {}\n ): Promise<T> {\n const messages: AIMessage[] =\n typeof prompt === 'string' ? [{ role: 'user', content: prompt }] : prompt\n\n const schemaHint = options.schema\n ? `\\n\\nResponse MUST conform to this JSON schema:\\n${JSON.stringify(options.schema)}`\n : ''\n const enforcedSystem =\n (options.systemPrompt ? options.systemPrompt + '\\n\\n' : '') +\n 'Return only valid JSON. No markdown code fences, no commentary.' +\n schemaHint\n\n const text = await this.chat(messages, { ...options, systemPrompt: enforcedSystem })\n return parseJSONResponse<T>(text)\n }\n\n async vision(\n image: Buffer | Uint8Array | { url: string },\n prompt: string,\n options: AIVisionOptions = {}\n ): Promise<string> {\n const imagePart: AIContentPart =\n typeof image === 'object' && 'url' in image\n ? { type: 'image', source: { kind: 'url', url: image.url } }\n : {\n type: 'image',\n source: { kind: 'base64', data: image as Buffer | Uint8Array, mediaType: 'image/png' }\n }\n\n return this.chat(\n [{ role: 'user', content: [imagePart, { type: 'text', text: prompt }] }],\n options\n )\n }\n\n async chatWithTools(\n messages: AIMessage[],\n tools: AITool[],\n options: AIToolChatOptions = {}\n ): Promise<AIToolChatResult> {\n const systemFromMessages = messages\n .filter(m => m.role === 'system')\n .map(extractTextContent)\n .filter(Boolean)\n .join('\\n\\n')\n const system = [options.systemPrompt, systemFromMessages].filter(Boolean).join('\\n\\n')\n const conversation = messages.filter(m => m.role !== 'system').map(toAnthropicMessage)\n\n const sdkTools = tools.map(t => ({\n name: t.name,\n description: t.description,\n input_schema: t.parameters as any\n }))\n\n const toolChoice = buildAnthropicToolChoice(options)\n\n const response = await this.sdk.messages.create({\n model: options.model ?? this.defaultModel,\n max_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(system && { system }),\n ...(options.stopSequences && { stop_sequences: options.stopSequences }),\n messages: conversation,\n tools: sdkTools,\n ...(toolChoice && { tool_choice: toolChoice })\n } as any)\n\n const text = extractResponseText(response) || undefined\n const toolCalls: AIToolCall[] = Array.isArray(response.content)\n ? response.content\n .filter((b: any) => b && b.type === 'tool_use')\n .map((b: any) => ({\n id: typeof b.id === 'string' && b.id ? b.id : `call_${Math.random().toString(36).slice(2, 12)}`,\n name: b.name,\n arguments: (b.input && typeof b.input === 'object' ? b.input : {}) as Record<string, any>\n }))\n : []\n\n return {\n text,\n toolCalls,\n stopReason: normalizeAnthropicStopReason(response.stop_reason)\n }\n }\n}\n\n// ── 변환 유틸 ─────────────────────────────────────────────────────────\n\nfunction extractTextContent(msg: AIMessage): string {\n if (typeof msg.content === 'string') return msg.content\n return msg.content\n .filter(p => p.type === 'text')\n .map(p => (p as { type: 'text'; text: string }).text)\n .join('\\n')\n}\n\nfunction toAnthropicMessage(msg: AIMessage): any {\n if (typeof msg.content === 'string') {\n return { role: msg.role, content: msg.content }\n }\n return {\n role: msg.role,\n content: msg.content.map(toAnthropicContentBlock)\n }\n}\n\nfunction toAnthropicContentBlock(part: AIContentPart): any {\n if (part.type === 'text') {\n return { type: 'text', text: part.text }\n }\n if (part.type === 'tool_use') {\n return { type: 'tool_use', id: part.id, name: part.name, input: part.arguments }\n }\n if (part.type === 'tool_result') {\n return {\n type: 'tool_result',\n tool_use_id: part.toolUseId,\n content: part.content,\n ...(part.isError && { is_error: true })\n }\n }\n // image\n if (part.source.kind === 'url') {\n return { type: 'image', source: { type: 'url', url: part.source.url } }\n }\n const data = Buffer.isBuffer(part.source.data)\n ? part.source.data.toString('base64')\n : Buffer.from(part.source.data).toString('base64')\n return {\n type: 'image',\n source: { type: 'base64', media_type: part.source.mediaType, data }\n }\n}\n\nfunction extractResponseText(response: any): string {\n if (!response || !Array.isArray(response.content)) return ''\n return response.content\n .filter((b: any) => b && b.type === 'text')\n .map((b: any) => b.text || '')\n .join('')\n}\n\nfunction buildAnthropicToolChoice(options: AIToolChatOptions): any | undefined {\n const choice = options.toolChoice ?? 'auto'\n const disableParallel = options.allowParallelToolCalls === false\n if (choice === 'none') {\n return { type: 'none' }\n }\n if (choice === 'required') {\n if (options.forceTool) {\n return {\n type: 'tool',\n name: options.forceTool,\n ...(disableParallel && { disable_parallel_tool_use: true })\n }\n }\n return {\n type: 'any',\n ...(disableParallel && { disable_parallel_tool_use: true })\n }\n }\n // 'auto'\n return {\n type: 'auto',\n ...(disableParallel && { disable_parallel_tool_use: true })\n }\n}\n\nfunction normalizeAnthropicStopReason(raw: any): AIToolChatResult['stopReason'] {\n switch (raw) {\n case 'tool_use':\n return 'tool_use'\n case 'end_turn':\n return 'end_turn'\n case 'max_tokens':\n return 'max_tokens'\n case 'stop_sequence':\n return 'stop_sequence'\n default:\n return raw ?? 'end_turn'\n }\n}\n\n// Auto-register at module load\nregisterAIProvider('anthropic', config => new AnthropicAIClient(config))\n"]}
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../server/service/providers/anthropic.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Anthropic (Claude) AI Client adapter.\n *\n * 기본 모델: claude-sonnet-4-6 (구성에서 override 가능).\n * SDK: @anthropic-ai/sdk\n */\nimport Anthropic from '@anthropic-ai/sdk'\nimport type {\n AIChatOptions,\n AIClient,\n AIClientConfig,\n AIContentPart,\n AIJSONOptions,\n AIMessage,\n AITool,\n AIToolCall,\n AIToolChatOptions,\n AIToolChatResult,\n AIVisionOptions\n} from '../types.js'\nimport { registerAIProvider } from '../factory.js'\nimport { parseJSONResponse } from '../utils/parse-json.js'\n\nconst DEFAULT_MODEL = 'claude-sonnet-4-6'\nconst DEFAULT_MAX_TOKENS = 4096\n\nexport class AnthropicAIClient implements AIClient {\n readonly id: string\n private sdk: Anthropic\n private defaultModel: string\n\n constructor(config: AIClientConfig) {\n this.sdk = new Anthropic({\n apiKey: config.apiKey,\n ...(config.baseURL && { baseURL: config.baseURL })\n })\n this.defaultModel = config.model ?? DEFAULT_MODEL\n this.id = `anthropic:${this.defaultModel}`\n }\n\n async chat(messages: AIMessage[], options: AIChatOptions = {}): Promise<string> {\n const systemFromMessages = messages\n .filter(m => m.role === 'system')\n .map(extractTextContent)\n .filter(Boolean)\n .join('\\n\\n')\n const system = [options.systemPrompt, systemFromMessages].filter(Boolean).join('\\n\\n')\n\n const conversation = messages\n .filter(m => m.role !== 'system')\n .map(toAnthropicMessage)\n\n const response = await this.sdk.messages.create({\n model: options.model ?? this.defaultModel,\n max_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(system && { system }),\n ...(options.stopSequences && { stop_sequences: options.stopSequences }),\n messages: conversation\n } as any)\n reportUsage(response, options, options.model ?? this.defaultModel)\n\n return extractResponseText(response)\n }\n\n async generateJSON<T = unknown>(\n prompt: string | AIMessage[],\n options: AIJSONOptions = {}\n ): Promise<T> {\n const messages: AIMessage[] =\n typeof prompt === 'string' ? [{ role: 'user', content: prompt }] : prompt\n\n const schemaHint = options.schema\n ? `\\n\\nResponse MUST conform to this JSON schema:\\n${JSON.stringify(options.schema)}`\n : ''\n const enforcedSystem =\n (options.systemPrompt ? options.systemPrompt + '\\n\\n' : '') +\n 'Return only valid JSON. No markdown code fences, no commentary.' +\n schemaHint\n\n const text = await this.chat(messages, { ...options, systemPrompt: enforcedSystem })\n return parseJSONResponse<T>(text)\n }\n\n async vision(\n image: Buffer | Uint8Array | { url: string },\n prompt: string,\n options: AIVisionOptions = {}\n ): Promise<string> {\n const imagePart: AIContentPart =\n typeof image === 'object' && 'url' in image\n ? { type: 'image', source: { kind: 'url', url: image.url } }\n : {\n type: 'image',\n source: { kind: 'base64', data: image as Buffer | Uint8Array, mediaType: 'image/png' }\n }\n\n return this.chat(\n [{ role: 'user', content: [imagePart, { type: 'text', text: prompt }] }],\n options\n )\n }\n\n async chatWithTools(\n messages: AIMessage[],\n tools: AITool[],\n options: AIToolChatOptions = {}\n ): Promise<AIToolChatResult> {\n const systemFromMessages = messages\n .filter(m => m.role === 'system')\n .map(extractTextContent)\n .filter(Boolean)\n .join('\\n\\n')\n const system = [options.systemPrompt, systemFromMessages].filter(Boolean).join('\\n\\n')\n const conversation = messages.filter(m => m.role !== 'system').map(toAnthropicMessage)\n\n const sdkTools = tools.map(t => ({\n name: t.name,\n description: t.description,\n input_schema: t.parameters as any\n }))\n\n const toolChoice = buildAnthropicToolChoice(options)\n\n const response = await this.sdk.messages.create({\n model: options.model ?? this.defaultModel,\n max_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(system && { system }),\n ...(options.stopSequences && { stop_sequences: options.stopSequences }),\n messages: conversation,\n tools: sdkTools,\n ...(toolChoice && { tool_choice: toolChoice })\n } as any)\n reportUsage(response, options, options.model ?? this.defaultModel)\n\n const text = extractResponseText(response) || undefined\n const toolCalls: AIToolCall[] = Array.isArray(response.content)\n ? response.content\n .filter((b: any) => b && b.type === 'tool_use')\n .map((b: any) => ({\n id: typeof b.id === 'string' && b.id ? b.id : `call_${Math.random().toString(36).slice(2, 12)}`,\n name: b.name,\n arguments: (b.input && typeof b.input === 'object' ? b.input : {}) as Record<string, any>\n }))\n : []\n\n return {\n text,\n toolCalls,\n stopReason: normalizeAnthropicStopReason(response.stop_reason)\n }\n }\n\n /** Every model Anthropic lists is a conversation model. */\n async listModels(): Promise<string[]> {\n const ids: string[] = []\n for await (const model of this.sdk.models.list()) ids.push(model.id)\n return ids.sort()\n }\n}\n\nfunction reportUsage(response: any, options: AIChatOptions, requested: string): void {\n options.onUsage?.({\n model: response?.model ?? requested,\n inputTokens: response?.usage?.input_tokens,\n outputTokens: response?.usage?.output_tokens\n })\n}\n\n// ── 변환 유틸 ─────────────────────────────────────────────────────────\n\nfunction extractTextContent(msg: AIMessage): string {\n if (typeof msg.content === 'string') return msg.content\n return msg.content\n .filter(p => p.type === 'text')\n .map(p => (p as { type: 'text'; text: string }).text)\n .join('\\n')\n}\n\nfunction toAnthropicMessage(msg: AIMessage): any {\n if (typeof msg.content === 'string') {\n return { role: msg.role, content: msg.content }\n }\n return {\n role: msg.role,\n content: msg.content.map(toAnthropicContentBlock)\n }\n}\n\nfunction toAnthropicContentBlock(part: AIContentPart): any {\n if (part.type === 'text') {\n return { type: 'text', text: part.text }\n }\n if (part.type === 'tool_use') {\n return { type: 'tool_use', id: part.id, name: part.name, input: part.arguments }\n }\n if (part.type === 'tool_result') {\n return {\n type: 'tool_result',\n tool_use_id: part.toolUseId,\n content: part.content,\n ...(part.isError && { is_error: true })\n }\n }\n // image\n if (part.source.kind === 'url') {\n return { type: 'image', source: { type: 'url', url: part.source.url } }\n }\n const data = Buffer.isBuffer(part.source.data)\n ? part.source.data.toString('base64')\n : Buffer.from(part.source.data).toString('base64')\n return {\n type: 'image',\n source: { type: 'base64', media_type: part.source.mediaType, data }\n }\n}\n\nfunction extractResponseText(response: any): string {\n if (!response || !Array.isArray(response.content)) return ''\n return response.content\n .filter((b: any) => b && b.type === 'text')\n .map((b: any) => b.text || '')\n .join('')\n}\n\nfunction buildAnthropicToolChoice(options: AIToolChatOptions): any | undefined {\n const choice = options.toolChoice ?? 'auto'\n const disableParallel = options.allowParallelToolCalls === false\n if (choice === 'none') {\n return { type: 'none' }\n }\n if (choice === 'required') {\n if (options.forceTool) {\n return {\n type: 'tool',\n name: options.forceTool,\n ...(disableParallel && { disable_parallel_tool_use: true })\n }\n }\n return {\n type: 'any',\n ...(disableParallel && { disable_parallel_tool_use: true })\n }\n }\n // 'auto'\n return {\n type: 'auto',\n ...(disableParallel && { disable_parallel_tool_use: true })\n }\n}\n\nfunction normalizeAnthropicStopReason(raw: any): AIToolChatResult['stopReason'] {\n switch (raw) {\n case 'tool_use':\n return 'tool_use'\n case 'end_turn':\n return 'end_turn'\n case 'max_tokens':\n return 'max_tokens'\n case 'stop_sequence':\n return 'stop_sequence'\n default:\n return raw ?? 'end_turn'\n }\n}\n\n// Auto-register at module load\nregisterAIProvider('anthropic', config => new AnthropicAIClient(config))\n"]}
@@ -3,6 +3,8 @@ export declare class GeminiAIClient implements AIClient {
3
3
  readonly id: string;
4
4
  private sdk;
5
5
  private defaultModel;
6
+ private apiKey;
7
+ private baseURL?;
6
8
  constructor(config: AIClientConfig);
7
9
  chat(messages: AIMessage[], options?: AIChatOptions): Promise<string>;
8
10
  generateJSON<T = unknown>(prompt: string | AIMessage[], options?: AIJSONOptions): Promise<T>;
@@ -11,4 +13,15 @@ export declare class GeminiAIClient implements AIClient {
11
13
  }, prompt: string, options?: AIVisionOptions): Promise<string>;
12
14
  private buildModel;
13
15
  chatWithTools(messages: AIMessage[], tools: AITool[], options?: AIToolChatOptions): Promise<AIToolChatResult>;
16
+ /**
17
+ * The SDK has no model list, so this reads the REST one. A conversation model is one that answers
18
+ * generateContent; embedding and other models are left out.
19
+ */
20
+ listModels(): Promise<string[]>;
21
+ private get requestOptions();
22
+ }
23
+ /** A model list the provider refused, with the HTTP status the caller turns into a reason. */
24
+ export declare class ProviderListError extends Error {
25
+ readonly status: number;
26
+ constructor(status: number, body: string);
14
27
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GeminiAIClient = void 0;
3
+ exports.ProviderListError = exports.GeminiAIClient = void 0;
4
4
  /**
5
5
  * Google Gemini AI Client adapter.
6
6
  *
@@ -20,14 +20,9 @@ const DEFAULT_MODEL = 'gemini-3-flash-preview';
20
20
  const DEFAULT_MAX_TOKENS = 8192;
21
21
  class GeminiAIClient {
22
22
  constructor(config) {
23
- // Gemini SDK 는 환경변수 자동 인식이 없으므로 어댑터에서 명시적으로 fallback.
24
- // 우선순위: config.apiKey → GOOGLE_API_KEY → GEMINI_API_KEY → GOOGLE_GENERATIVE_AI_API_KEY
25
- const apiKey = config.apiKey ??
26
- process.env.GOOGLE_API_KEY ??
27
- process.env.GEMINI_API_KEY ??
28
- process.env.GOOGLE_GENERATIVE_AI_API_KEY ??
29
- '';
30
- this.sdk = new generative_ai_1.GoogleGenerativeAI(apiKey);
23
+ this.apiKey = config.apiKey;
24
+ this.baseURL = config.baseURL;
25
+ this.sdk = new generative_ai_1.GoogleGenerativeAI(config.apiKey);
31
26
  this.defaultModel = config.model ?? DEFAULT_MODEL;
32
27
  this.id = `gemini:${this.defaultModel}`;
33
28
  }
@@ -35,6 +30,7 @@ class GeminiAIClient {
35
30
  const { systemPrompt, contents } = splitMessages(messages, options.systemPrompt);
36
31
  const model = this.buildModel(options, systemPrompt);
37
32
  const result = await model.generateContent({ contents });
33
+ reportUsage(result, options, options.model ?? this.defaultModel);
38
34
  return result.response.text();
39
35
  }
40
36
  async generateJSON(prompt, options = {}) {
@@ -48,8 +44,9 @@ class GeminiAIClient {
48
44
  model: options.model ?? this.defaultModel,
49
45
  ...(systemPrompt && { systemInstruction: systemPrompt }),
50
46
  generationConfig
51
- });
47
+ }, this.requestOptions);
52
48
  const result = await model.generateContent({ contents });
49
+ reportUsage(result, options, options.model ?? this.defaultModel);
53
50
  const text = result.response.text();
54
51
  return (0, parse_json_js_1.parseJSONResponse)(text);
55
52
  }
@@ -67,7 +64,7 @@ class GeminiAIClient {
67
64
  model: options.model ?? this.defaultModel,
68
65
  ...(systemInstruction && { systemInstruction }),
69
66
  generationConfig: baseGenerationConfig(options)
70
- });
67
+ }, this.requestOptions);
71
68
  }
72
69
  async chatWithTools(messages, tools, options = {}) {
73
70
  const { systemPrompt, contents } = splitMessages(messages, options.systemPrompt);
@@ -85,8 +82,9 @@ class GeminiAIClient {
85
82
  generationConfig: baseGenerationConfig(options),
86
83
  tools: [{ functionDeclarations }],
87
84
  ...(toolConfig && { toolConfig })
88
- });
85
+ }, this.requestOptions);
89
86
  const result = await model.generateContent({ contents });
87
+ reportUsage(result, options, options.model ?? this.defaultModel);
90
88
  const response = result.response;
91
89
  const candidate = response?.candidates?.[0];
92
90
  const parts = candidate?.content?.parts ?? [];
@@ -127,8 +125,50 @@ class GeminiAIClient {
127
125
  stopReason: normalizeGeminiStopReason(candidate?.finishReason, toolCalls.length > 0)
128
126
  };
129
127
  }
128
+ /**
129
+ * The SDK has no model list, so this reads the REST one. A conversation model is one that answers
130
+ * generateContent; embedding and other models are left out.
131
+ */
132
+ async listModels() {
133
+ const base = (this.baseURL || 'https://generativelanguage.googleapis.com').replace(/\/+$/, '');
134
+ const ids = [];
135
+ let pageToken = '';
136
+ do {
137
+ const url = `${base}/v1beta/models?pageSize=1000${pageToken ? `&pageToken=${encodeURIComponent(pageToken)}` : ''}`;
138
+ const response = await fetch(url, { headers: { 'x-goog-api-key': this.apiKey } });
139
+ if (!response.ok)
140
+ throw new ProviderListError(response.status, await response.text());
141
+ const page = await response.json();
142
+ for (const model of page.models ?? []) {
143
+ if ((model.supportedGenerationMethods ?? []).includes('generateContent')) {
144
+ ids.push(String(model.name).replace(/^models\//, ''));
145
+ }
146
+ }
147
+ pageToken = page.nextPageToken ?? '';
148
+ } while (pageToken);
149
+ return ids.sort();
150
+ }
151
+ get requestOptions() {
152
+ return this.baseURL ? { baseUrl: this.baseURL } : undefined;
153
+ }
130
154
  }
131
155
  exports.GeminiAIClient = GeminiAIClient;
156
+ /** A model list the provider refused, with the HTTP status the caller turns into a reason. */
157
+ class ProviderListError extends Error {
158
+ constructor(status, body) {
159
+ super(`model list failed with ${status}: ${body.slice(0, 200)}`);
160
+ this.status = status;
161
+ }
162
+ }
163
+ exports.ProviderListError = ProviderListError;
164
+ function reportUsage(result, options, requested) {
165
+ const usage = result?.response?.usageMetadata;
166
+ options.onUsage?.({
167
+ model: result?.response?.modelVersion ?? requested,
168
+ inputTokens: usage?.promptTokenCount,
169
+ outputTokens: usage?.candidatesTokenCount
170
+ });
171
+ }
132
172
  // ── 변환 유틸 ─────────────────────────────────────────────────────────
133
173
  function baseGenerationConfig(options) {
134
174
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../../server/service/providers/gemini.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Google Gemini AI Client adapter.\n *\n * 기본 모델: gemini-3-pro (구성에서 override 가능).\n * SDK: @google/generative-ai\n *\n * 비고: Antigravity (Google 의 agentic IDE) 가 기본 백엔드로 사용하는 모델군.\n * 이 어댑터는 Gemini API 직접 호출 — Antigravity IDE 와는 별개.\n */\nimport { GoogleGenerativeAI } from '@google/generative-ai'\nimport type {\n AIChatOptions,\n AIClient,\n AIClientConfig,\n AIContentPart,\n AIJSONOptions,\n AIMessage,\n AITool,\n AIToolCall,\n AIToolChatOptions,\n AIToolChatResult,\n AIVisionOptions\n} from '../types.js'\nimport { registerAIProvider } from '../factory.js'\nimport { parseJSONResponse } from '../utils/parse-json.js'\n\n// `gemini-3-pro` 는 2026-03-09 종료됨. 후속 권장 모델로 변경.\n// 사용자가 자기 환경에서 활용 가능한 정확한 ID 는 ListModels 로 확인:\n// curl \"https://generativelanguage.googleapis.com/v1beta/models?key=$KEY\"\nconst DEFAULT_MODEL = 'gemini-3-flash-preview'\nconst DEFAULT_MAX_TOKENS = 8192\n\nexport class GeminiAIClient implements AIClient {\n readonly id: string\n private sdk: GoogleGenerativeAI\n private defaultModel: string\n\n constructor(config: AIClientConfig) {\n // Gemini SDK 는 환경변수 자동 인식이 없으므로 어댑터에서 명시적으로 fallback.\n // 우선순위: config.apiKey → GOOGLE_API_KEY → GEMINI_API_KEY → GOOGLE_GENERATIVE_AI_API_KEY\n const apiKey =\n config.apiKey ??\n process.env.GOOGLE_API_KEY ??\n process.env.GEMINI_API_KEY ??\n process.env.GOOGLE_GENERATIVE_AI_API_KEY ??\n ''\n this.sdk = new GoogleGenerativeAI(apiKey)\n this.defaultModel = config.model ?? DEFAULT_MODEL\n this.id = `gemini:${this.defaultModel}`\n }\n\n async chat(messages: AIMessage[], options: AIChatOptions = {}): Promise<string> {\n const { systemPrompt, contents } = splitMessages(messages, options.systemPrompt)\n const model = this.buildModel(options, systemPrompt)\n const result = await model.generateContent({ contents } as any)\n return result.response.text()\n }\n\n async generateJSON<T = unknown>(\n prompt: string | AIMessage[],\n options: AIJSONOptions = {}\n ): Promise<T> {\n const messages: AIMessage[] =\n typeof prompt === 'string' ? [{ role: 'user', content: prompt }] : prompt\n const { systemPrompt, contents } = splitMessages(messages, options.systemPrompt)\n\n const generationConfig: any = baseGenerationConfig(options)\n generationConfig.responseMimeType = 'application/json'\n if (options.schema) generationConfig.responseSchema = options.schema\n\n const model = this.sdk.getGenerativeModel({\n model: options.model ?? this.defaultModel,\n ...(systemPrompt && { systemInstruction: systemPrompt }),\n generationConfig\n })\n const result = await model.generateContent({ contents } as any)\n const text = result.response.text()\n return parseJSONResponse<T>(text)\n }\n\n async vision(\n image: Buffer | Uint8Array | { url: string },\n prompt: string,\n options: AIVisionOptions = {}\n ): Promise<string> {\n const imagePart: AIContentPart =\n typeof image === 'object' && 'url' in image\n ? { type: 'image', source: { kind: 'url', url: image.url } }\n : {\n type: 'image',\n source: { kind: 'base64', data: image as Buffer | Uint8Array, mediaType: 'image/png' }\n }\n\n return this.chat(\n [{ role: 'user', content: [imagePart, { type: 'text', text: prompt }] }],\n options\n )\n }\n\n private buildModel(options: AIChatOptions, systemInstruction?: string) {\n return this.sdk.getGenerativeModel({\n model: options.model ?? this.defaultModel,\n ...(systemInstruction && { systemInstruction }),\n generationConfig: baseGenerationConfig(options)\n })\n }\n\n async chatWithTools(\n messages: AIMessage[],\n tools: AITool[],\n options: AIToolChatOptions = {}\n ): Promise<AIToolChatResult> {\n const { systemPrompt, contents } = splitMessages(messages, options.systemPrompt)\n\n // Gemini 는 OpenAPI 3.0 subset — JSON Schema 의 일부 키를 거부함.\n // 'additionalProperties' / '$schema' / '$defs' 등을 제거.\n const functionDeclarations = tools.map(t => ({\n name: t.name,\n description: t.description,\n parameters: sanitizeForGeminiSchema(t.parameters)\n }))\n\n const toolConfig = buildGeminiToolConfig(options)\n\n const model = this.sdk.getGenerativeModel({\n model: options.model ?? this.defaultModel,\n ...(systemPrompt && { systemInstruction: systemPrompt }),\n generationConfig: baseGenerationConfig(options),\n tools: [{ functionDeclarations }] as any,\n ...(toolConfig && { toolConfig })\n } as any)\n\n const result = await model.generateContent({ contents } as any)\n const response: any = result.response\n\n const candidate = response?.candidates?.[0]\n const parts = candidate?.content?.parts ?? []\n\n // Gemini 는 한 응답에서 (a) thinking part (p.thought === true) 와\n // (b) 본문 text part 를 함께 emit 할 수 있다. thought 는 사용자에게 노출하면\n // \"구조화 답변 + 산문 답변\" 형태의 중복으로 보이므로 제거.\n const textParts = parts\n .filter((p: any) => typeof p?.text === 'string' && p?.thought !== true)\n .map((p: any) => p.text)\n const text = textParts.join('') || undefined\n\n // Gemini 는 functionCall 에 자체 id 가 없음 — 어댑터가 turn 내부에서 부여.\n // id 포맷 'g:<name>:<seq>' — assistant 가 tool_result 를 만들 때 이 id 를 그대로\n // 넘기면 toGeminiPart 가 name 을 추출해 functionResponse.name 에 채운다.\n //\n // thoughtSignature: Gemini 2.5/3 부터 thinking 모델이 functionCall 에 서명을 붙임.\n // 다음 turn 에 그대로 echo 해야 400 에러 안 남 (cite: ai.google.dev/.../thought-signatures).\n let geminiCallSeq = 0\n const ts = Date.now()\n const toolCalls: AIToolCall[] = parts\n .filter((p: any) => p?.functionCall && typeof p.functionCall.name === 'string')\n .map((p: any) => {\n // SDK 가 camelCase 또는 snake_case 둘 중 하나로 emit — 둘 다 받음.\n const sig: string | undefined = p.thoughtSignature ?? p.thought_signature\n return {\n id: `g:${p.functionCall.name}:${ts}_${geminiCallSeq++}`,\n name: p.functionCall.name,\n arguments:\n p.functionCall.args && typeof p.functionCall.args === 'object'\n ? (p.functionCall.args as Record<string, any>)\n : {},\n ...(typeof sig === 'string' && sig.length > 0 && {\n providerMeta: { thoughtSignature: sig }\n })\n }\n })\n\n return {\n text,\n toolCalls,\n stopReason: normalizeGeminiStopReason(candidate?.finishReason, toolCalls.length > 0)\n }\n }\n}\n\n// ── 변환 유틸 ─────────────────────────────────────────────────────────\n\nfunction baseGenerationConfig(options: AIChatOptions): any {\n return {\n maxOutputTokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(options.stopSequences && { stopSequences: options.stopSequences })\n }\n}\n\nfunction splitMessages(\n messages: AIMessage[],\n externalSystem?: string\n): { systemPrompt: string; contents: any[] } {\n const systemMessages = messages\n .filter(m => m.role === 'system')\n .map(extractTextContent)\n .filter(Boolean)\n const conversation = messages.filter(m => m.role !== 'system')\n const systemPrompt = [externalSystem, ...systemMessages].filter(Boolean).join('\\n\\n')\n return { systemPrompt, contents: conversation.map(toGeminiContent) }\n}\n\nfunction extractTextContent(msg: AIMessage): string {\n if (typeof msg.content === 'string') return msg.content\n return msg.content\n .filter(p => p.type === 'text')\n .map(p => (p as { type: 'text'; text: string }).text)\n .join('\\n')\n}\n\nfunction toGeminiContent(msg: AIMessage): any {\n // Our 'assistant' role → Gemini's 'model' role\n const role = msg.role === 'assistant' ? 'model' : msg.role\n const parts =\n typeof msg.content === 'string'\n ? [{ text: msg.content }]\n : msg.content.map(toGeminiPart)\n return { role, parts }\n}\n\nfunction toGeminiPart(part: AIContentPart): any {\n if (part.type === 'text') {\n return { text: part.text }\n }\n if (part.type === 'tool_use') {\n // Gemini: assistant 가 호출 의도 표현. functionCall.name + args 만 가짐 (id 없음).\n // thoughtSignature 는 thinking 모델 사용 시 필수 — providerMeta 에서 복원해 echo.\n const sig = part.providerMeta?.thoughtSignature\n return {\n functionCall: { name: part.name, args: part.arguments },\n ...(typeof sig === 'string' && sig.length > 0 && { thoughtSignature: sig })\n }\n }\n if (part.type === 'tool_result') {\n // Gemini: user role 에 functionResponse part 로 회신. 이름은 호출했던 tool name 과\n // 같아야 함 — assistant.ts 의 loop 에서 part 생성 시 함께 전달.\n let parsed: any = part.content\n try {\n parsed = JSON.parse(part.content)\n } catch {\n parsed = { result: part.content }\n }\n return {\n functionResponse: {\n // name 은 원래 호출했던 tool name. toolUseId 에 임베드되어 있지 않으므로\n // assistant 가 메시지 생성 시 별도 채워줘야 함 — 여기서는 placeholder.\n // 실제 사용 시 toolUseId 를 'tool_<name>_<seq>' 형태로 만들어 name 추출.\n name: extractGeminiNameFromId(part.toolUseId),\n response: parsed\n }\n }\n }\n // image\n if (part.source.kind === 'url') {\n // Gemini SDK 는 inlineData(base64) 만 지원. URL 은 호출 전에 fetch 필요.\n throw new Error(\n 'Gemini adapter: image URL is not supported directly. Fetch the URL and pass as Buffer.'\n )\n }\n const data = Buffer.isBuffer(part.source.data)\n ? part.source.data.toString('base64')\n : Buffer.from(part.source.data).toString('base64')\n return {\n inlineData: { data, mimeType: part.source.mediaType }\n }\n}\n\n/**\n * Gemini 의 functionCall 에는 자체 id 가 없어서 chatWithTools 가 'gemini_<ts>_<seq>'\n * 형태의 가짜 id 를 만든다. 그런데 functionResponse 에는 name 이 필수 — assistant 의\n * tool 실행기는 이 id 와 name 을 별도 매핑해야 한다. assistant 가 tool_result part 를\n * 만들 때 toolUseId 에 'name:<actualName>;id:<originalId>' 같은 인코딩을 할 수 있지만\n * 그건 invasive — 대신 toolUseId 가 'g:<name>:<id>' 형태면 name 을 직접 추출.\n *\n * 현재는 fallback: id 가 'g:' 로 시작하면 두 번째 segment 를 name 으로,\n * 아니면 그대로 사용 (Gemini 가 받지 않으면 에러 — assistant 가 책임지고 인코딩).\n */\nfunction extractGeminiNameFromId(id: string): string {\n if (id.startsWith('g:')) {\n const parts = id.split(':')\n return parts[1] || id\n }\n return id\n}\n\n/**\n * JSON Schema → Gemini OpenAPI 3.0 subset 변환.\n * Gemini API 가 거부하는 키들 (deep) 제거.\n * 보존: type / properties / items / required / enum / description / format / nullable\n */\nconst GEMINI_REJECTED_KEYS = new Set([\n 'additionalProperties',\n '$schema',\n '$defs',\n '$ref',\n '$id',\n 'definitions',\n 'patternProperties',\n 'dependencies'\n])\n\nfunction sanitizeForGeminiSchema(schema: any): any {\n if (schema === null || schema === undefined) return schema\n if (Array.isArray(schema)) return schema.map(sanitizeForGeminiSchema)\n if (typeof schema !== 'object') return schema\n const out: any = {}\n for (const [k, v] of Object.entries(schema)) {\n if (GEMINI_REJECTED_KEYS.has(k)) continue\n out[k] = sanitizeForGeminiSchema(v)\n }\n return out\n}\n\nfunction buildGeminiToolConfig(options: AIToolChatOptions): any | undefined {\n const choice = options.toolChoice ?? 'auto'\n if (choice === 'none') {\n return { functionCallingConfig: { mode: 'NONE' } }\n }\n if (choice === 'required') {\n return {\n functionCallingConfig: {\n mode: 'ANY',\n ...(options.forceTool && { allowedFunctionNames: [options.forceTool] })\n }\n }\n }\n return { functionCallingConfig: { mode: 'AUTO' } }\n}\n\nfunction normalizeGeminiStopReason(\n raw: any,\n hasToolCalls: boolean\n): AIToolChatResult['stopReason'] {\n // Gemini 는 function call 발생 시 finishReason 을 'STOP' 으로 마킹할 수 있어\n // toolCalls 존재 여부로 우선 판정.\n if (hasToolCalls) return 'tool_use'\n switch (raw) {\n case 'STOP':\n return 'end_turn'\n case 'MAX_TOKENS':\n return 'max_tokens'\n case 'SAFETY':\n case 'RECITATION':\n case 'OTHER':\n return 'stop_sequence'\n default:\n return raw ?? 'end_turn'\n }\n}\n\n// Auto-register at module load\nregisterAIProvider('gemini', config => new GeminiAIClient(config))\n"]}
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../../server/service/providers/gemini.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Google Gemini AI Client adapter.\n *\n * 기본 모델: gemini-3-pro (구성에서 override 가능).\n * SDK: @google/generative-ai\n *\n * 비고: Antigravity (Google 의 agentic IDE) 가 기본 백엔드로 사용하는 모델군.\n * 이 어댑터는 Gemini API 직접 호출 — Antigravity IDE 와는 별개.\n */\nimport { GoogleGenerativeAI } from '@google/generative-ai'\nimport type {\n AIChatOptions,\n AIClient,\n AIClientConfig,\n AIContentPart,\n AIJSONOptions,\n AIMessage,\n AITool,\n AIToolCall,\n AIToolChatOptions,\n AIToolChatResult,\n AIVisionOptions\n} from '../types.js'\nimport { registerAIProvider } from '../factory.js'\nimport { parseJSONResponse } from '../utils/parse-json.js'\n\n// `gemini-3-pro` 는 2026-03-09 종료됨. 후속 권장 모델로 변경.\n// 사용자가 자기 환경에서 활용 가능한 정확한 ID 는 ListModels 로 확인:\n// curl \"https://generativelanguage.googleapis.com/v1beta/models?key=$KEY\"\nconst DEFAULT_MODEL = 'gemini-3-flash-preview'\nconst DEFAULT_MAX_TOKENS = 8192\n\nexport class GeminiAIClient implements AIClient {\n readonly id: string\n private sdk: GoogleGenerativeAI\n private defaultModel: string\n private apiKey: string\n private baseURL?: string\n\n constructor(config: AIClientConfig) {\n this.apiKey = config.apiKey\n this.baseURL = config.baseURL\n this.sdk = new GoogleGenerativeAI(config.apiKey)\n this.defaultModel = config.model ?? DEFAULT_MODEL\n this.id = `gemini:${this.defaultModel}`\n }\n\n async chat(messages: AIMessage[], options: AIChatOptions = {}): Promise<string> {\n const { systemPrompt, contents } = splitMessages(messages, options.systemPrompt)\n const model = this.buildModel(options, systemPrompt)\n const result = await model.generateContent({ contents } as any)\n reportUsage(result, options, options.model ?? this.defaultModel)\n return result.response.text()\n }\n\n async generateJSON<T = unknown>(\n prompt: string | AIMessage[],\n options: AIJSONOptions = {}\n ): Promise<T> {\n const messages: AIMessage[] =\n typeof prompt === 'string' ? [{ role: 'user', content: prompt }] : prompt\n const { systemPrompt, contents } = splitMessages(messages, options.systemPrompt)\n\n const generationConfig: any = baseGenerationConfig(options)\n generationConfig.responseMimeType = 'application/json'\n if (options.schema) generationConfig.responseSchema = options.schema\n\n const model = this.sdk.getGenerativeModel({\n model: options.model ?? this.defaultModel,\n ...(systemPrompt && { systemInstruction: systemPrompt }),\n generationConfig\n }, this.requestOptions)\n const result = await model.generateContent({ contents } as any)\n reportUsage(result, options, options.model ?? this.defaultModel)\n const text = result.response.text()\n return parseJSONResponse<T>(text)\n }\n\n async vision(\n image: Buffer | Uint8Array | { url: string },\n prompt: string,\n options: AIVisionOptions = {}\n ): Promise<string> {\n const imagePart: AIContentPart =\n typeof image === 'object' && 'url' in image\n ? { type: 'image', source: { kind: 'url', url: image.url } }\n : {\n type: 'image',\n source: { kind: 'base64', data: image as Buffer | Uint8Array, mediaType: 'image/png' }\n }\n\n return this.chat(\n [{ role: 'user', content: [imagePart, { type: 'text', text: prompt }] }],\n options\n )\n }\n\n private buildModel(options: AIChatOptions, systemInstruction?: string) {\n return this.sdk.getGenerativeModel({\n model: options.model ?? this.defaultModel,\n ...(systemInstruction && { systemInstruction }),\n generationConfig: baseGenerationConfig(options)\n }, this.requestOptions)\n }\n\n async chatWithTools(\n messages: AIMessage[],\n tools: AITool[],\n options: AIToolChatOptions = {}\n ): Promise<AIToolChatResult> {\n const { systemPrompt, contents } = splitMessages(messages, options.systemPrompt)\n\n // Gemini 는 OpenAPI 3.0 subset — JSON Schema 의 일부 키를 거부함.\n // 'additionalProperties' / '$schema' / '$defs' 등을 제거.\n const functionDeclarations = tools.map(t => ({\n name: t.name,\n description: t.description,\n parameters: sanitizeForGeminiSchema(t.parameters)\n }))\n\n const toolConfig = buildGeminiToolConfig(options)\n\n const model = this.sdk.getGenerativeModel({\n model: options.model ?? this.defaultModel,\n ...(systemPrompt && { systemInstruction: systemPrompt }),\n generationConfig: baseGenerationConfig(options),\n tools: [{ functionDeclarations }] as any,\n ...(toolConfig && { toolConfig })\n } as any, this.requestOptions)\n\n const result = await model.generateContent({ contents } as any)\n reportUsage(result, options, options.model ?? this.defaultModel)\n const response: any = result.response\n\n const candidate = response?.candidates?.[0]\n const parts = candidate?.content?.parts ?? []\n\n // Gemini 는 한 응답에서 (a) thinking part (p.thought === true) 와\n // (b) 본문 text part 를 함께 emit 할 수 있다. thought 는 사용자에게 노출하면\n // \"구조화 답변 + 산문 답변\" 형태의 중복으로 보이므로 제거.\n const textParts = parts\n .filter((p: any) => typeof p?.text === 'string' && p?.thought !== true)\n .map((p: any) => p.text)\n const text = textParts.join('') || undefined\n\n // Gemini 는 functionCall 에 자체 id 가 없음 — 어댑터가 turn 내부에서 부여.\n // id 포맷 'g:<name>:<seq>' — assistant 가 tool_result 를 만들 때 이 id 를 그대로\n // 넘기면 toGeminiPart 가 name 을 추출해 functionResponse.name 에 채운다.\n //\n // thoughtSignature: Gemini 2.5/3 부터 thinking 모델이 functionCall 에 서명을 붙임.\n // 다음 turn 에 그대로 echo 해야 400 에러 안 남 (cite: ai.google.dev/.../thought-signatures).\n let geminiCallSeq = 0\n const ts = Date.now()\n const toolCalls: AIToolCall[] = parts\n .filter((p: any) => p?.functionCall && typeof p.functionCall.name === 'string')\n .map((p: any) => {\n // SDK 가 camelCase 또는 snake_case 둘 중 하나로 emit — 둘 다 받음.\n const sig: string | undefined = p.thoughtSignature ?? p.thought_signature\n return {\n id: `g:${p.functionCall.name}:${ts}_${geminiCallSeq++}`,\n name: p.functionCall.name,\n arguments:\n p.functionCall.args && typeof p.functionCall.args === 'object'\n ? (p.functionCall.args as Record<string, any>)\n : {},\n ...(typeof sig === 'string' && sig.length > 0 && {\n providerMeta: { thoughtSignature: sig }\n })\n }\n })\n\n return {\n text,\n toolCalls,\n stopReason: normalizeGeminiStopReason(candidate?.finishReason, toolCalls.length > 0)\n }\n }\n\n /**\n * The SDK has no model list, so this reads the REST one. A conversation model is one that answers\n * generateContent; embedding and other models are left out.\n */\n async listModels(): Promise<string[]> {\n const base = (this.baseURL || 'https://generativelanguage.googleapis.com').replace(/\\/+$/, '')\n const ids: string[] = []\n let pageToken = ''\n do {\n const url = `${base}/v1beta/models?pageSize=1000${pageToken ? `&pageToken=${encodeURIComponent(pageToken)}` : ''}`\n const response = await fetch(url, { headers: { 'x-goog-api-key': this.apiKey } })\n if (!response.ok) throw new ProviderListError(response.status, await response.text())\n const page: any = await response.json()\n for (const model of page.models ?? []) {\n if ((model.supportedGenerationMethods ?? []).includes('generateContent')) {\n ids.push(String(model.name).replace(/^models\\//, ''))\n }\n }\n pageToken = page.nextPageToken ?? ''\n } while (pageToken)\n return ids.sort()\n }\n\n private get requestOptions() {\n return this.baseURL ? { baseUrl: this.baseURL } : undefined\n }\n}\n\n/** A model list the provider refused, with the HTTP status the caller turns into a reason. */\nexport class ProviderListError extends Error {\n constructor(readonly status: number, body: string) {\n super(`model list failed with ${status}: ${body.slice(0, 200)}`)\n }\n}\n\nfunction reportUsage(result: any, options: AIChatOptions, requested: string): void {\n const usage = result?.response?.usageMetadata\n options.onUsage?.({\n model: result?.response?.modelVersion ?? requested,\n inputTokens: usage?.promptTokenCount,\n outputTokens: usage?.candidatesTokenCount\n })\n}\n\n// ── 변환 유틸 ─────────────────────────────────────────────────────────\n\nfunction baseGenerationConfig(options: AIChatOptions): any {\n return {\n maxOutputTokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(options.stopSequences && { stopSequences: options.stopSequences })\n }\n}\n\nfunction splitMessages(\n messages: AIMessage[],\n externalSystem?: string\n): { systemPrompt: string; contents: any[] } {\n const systemMessages = messages\n .filter(m => m.role === 'system')\n .map(extractTextContent)\n .filter(Boolean)\n const conversation = messages.filter(m => m.role !== 'system')\n const systemPrompt = [externalSystem, ...systemMessages].filter(Boolean).join('\\n\\n')\n return { systemPrompt, contents: conversation.map(toGeminiContent) }\n}\n\nfunction extractTextContent(msg: AIMessage): string {\n if (typeof msg.content === 'string') return msg.content\n return msg.content\n .filter(p => p.type === 'text')\n .map(p => (p as { type: 'text'; text: string }).text)\n .join('\\n')\n}\n\nfunction toGeminiContent(msg: AIMessage): any {\n // Our 'assistant' role → Gemini's 'model' role\n const role = msg.role === 'assistant' ? 'model' : msg.role\n const parts =\n typeof msg.content === 'string'\n ? [{ text: msg.content }]\n : msg.content.map(toGeminiPart)\n return { role, parts }\n}\n\nfunction toGeminiPart(part: AIContentPart): any {\n if (part.type === 'text') {\n return { text: part.text }\n }\n if (part.type === 'tool_use') {\n // Gemini: assistant 가 호출 의도 표현. functionCall.name + args 만 가짐 (id 없음).\n // thoughtSignature 는 thinking 모델 사용 시 필수 — providerMeta 에서 복원해 echo.\n const sig = part.providerMeta?.thoughtSignature\n return {\n functionCall: { name: part.name, args: part.arguments },\n ...(typeof sig === 'string' && sig.length > 0 && { thoughtSignature: sig })\n }\n }\n if (part.type === 'tool_result') {\n // Gemini: user role 에 functionResponse part 로 회신. 이름은 호출했던 tool name 과\n // 같아야 함 — assistant.ts 의 loop 에서 part 생성 시 함께 전달.\n let parsed: any = part.content\n try {\n parsed = JSON.parse(part.content)\n } catch {\n parsed = { result: part.content }\n }\n return {\n functionResponse: {\n // name 은 원래 호출했던 tool name. toolUseId 에 임베드되어 있지 않으므로\n // assistant 가 메시지 생성 시 별도 채워줘야 함 — 여기서는 placeholder.\n // 실제 사용 시 toolUseId 를 'tool_<name>_<seq>' 형태로 만들어 name 추출.\n name: extractGeminiNameFromId(part.toolUseId),\n response: parsed\n }\n }\n }\n // image\n if (part.source.kind === 'url') {\n // Gemini SDK 는 inlineData(base64) 만 지원. URL 은 호출 전에 fetch 필요.\n throw new Error(\n 'Gemini adapter: image URL is not supported directly. Fetch the URL and pass as Buffer.'\n )\n }\n const data = Buffer.isBuffer(part.source.data)\n ? part.source.data.toString('base64')\n : Buffer.from(part.source.data).toString('base64')\n return {\n inlineData: { data, mimeType: part.source.mediaType }\n }\n}\n\n/**\n * Gemini 의 functionCall 에는 자체 id 가 없어서 chatWithTools 가 'gemini_<ts>_<seq>'\n * 형태의 가짜 id 를 만든다. 그런데 functionResponse 에는 name 이 필수 — assistant 의\n * tool 실행기는 이 id 와 name 을 별도 매핑해야 한다. assistant 가 tool_result part 를\n * 만들 때 toolUseId 에 'name:<actualName>;id:<originalId>' 같은 인코딩을 할 수 있지만\n * 그건 invasive — 대신 toolUseId 가 'g:<name>:<id>' 형태면 name 을 직접 추출.\n *\n * 현재는 fallback: id 가 'g:' 로 시작하면 두 번째 segment 를 name 으로,\n * 아니면 그대로 사용 (Gemini 가 받지 않으면 에러 — assistant 가 책임지고 인코딩).\n */\nfunction extractGeminiNameFromId(id: string): string {\n if (id.startsWith('g:')) {\n const parts = id.split(':')\n return parts[1] || id\n }\n return id\n}\n\n/**\n * JSON Schema → Gemini OpenAPI 3.0 subset 변환.\n * Gemini API 가 거부하는 키들 (deep) 제거.\n * 보존: type / properties / items / required / enum / description / format / nullable\n */\nconst GEMINI_REJECTED_KEYS = new Set([\n 'additionalProperties',\n '$schema',\n '$defs',\n '$ref',\n '$id',\n 'definitions',\n 'patternProperties',\n 'dependencies'\n])\n\nfunction sanitizeForGeminiSchema(schema: any): any {\n if (schema === null || schema === undefined) return schema\n if (Array.isArray(schema)) return schema.map(sanitizeForGeminiSchema)\n if (typeof schema !== 'object') return schema\n const out: any = {}\n for (const [k, v] of Object.entries(schema)) {\n if (GEMINI_REJECTED_KEYS.has(k)) continue\n out[k] = sanitizeForGeminiSchema(v)\n }\n return out\n}\n\nfunction buildGeminiToolConfig(options: AIToolChatOptions): any | undefined {\n const choice = options.toolChoice ?? 'auto'\n if (choice === 'none') {\n return { functionCallingConfig: { mode: 'NONE' } }\n }\n if (choice === 'required') {\n return {\n functionCallingConfig: {\n mode: 'ANY',\n ...(options.forceTool && { allowedFunctionNames: [options.forceTool] })\n }\n }\n }\n return { functionCallingConfig: { mode: 'AUTO' } }\n}\n\nfunction normalizeGeminiStopReason(\n raw: any,\n hasToolCalls: boolean\n): AIToolChatResult['stopReason'] {\n // Gemini 는 function call 발생 시 finishReason 을 'STOP' 으로 마킹할 수 있어\n // toolCalls 존재 여부로 우선 판정.\n if (hasToolCalls) return 'tool_use'\n switch (raw) {\n case 'STOP':\n return 'end_turn'\n case 'MAX_TOKENS':\n return 'max_tokens'\n case 'SAFETY':\n case 'RECITATION':\n case 'OTHER':\n return 'stop_sequence'\n default:\n return raw ?? 'end_turn'\n }\n}\n\n// Auto-register at module load\nregisterAIProvider('gemini', config => new GeminiAIClient(config))\n"]}
@@ -10,7 +10,13 @@ export declare class OpenAIAIClient implements AIClient {
10
10
  url: string;
11
11
  }, prompt: string, options?: AIVisionOptions): Promise<string>;
12
12
  chatWithTools(messages: AIMessage[], tools: AITool[], options?: AIToolChatOptions): Promise<AIToolChatResult>;
13
+ listModels(): Promise<string[]>;
13
14
  }
15
+ /**
16
+ * OpenAI's model list has no capability field, so the conversation models are told apart by name: the GPT and
17
+ * o-series families, without their audio, realtime, speech, image, search and instruct variants. One rule, kept by a test.
18
+ */
19
+ export declare function isOpenAIChatModel(id: string): boolean;
14
20
  /**
15
21
  * 한 AIMessage 가 다중 tool_result 를 가지면 OpenAI 측에서 여러 role:'tool' 메시지로
16
22
  * 분리해야 함 (OpenAI 는 메시지당 tool_call_id 하나만 매핑). 따라서 array 를 반환.
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OpenAIAIClient = void 0;
4
+ exports.isOpenAIChatModel = isOpenAIChatModel;
4
5
  exports.toSDKMessages = toSDKMessages;
5
6
  const tslib_1 = require("tslib");
6
7
  /**
@@ -9,8 +10,7 @@ const tslib_1 = require("tslib");
9
10
  * 기본 모델: gpt-4o (구성에서 override 가능).
10
11
  * SDK: openai
11
12
  *
12
- * 비고: legacy `openai` config 키 (codelingua 의 사용처) 도 factory.ts 가 이 어댑터로 자동 매핑.
13
- * organization 필드 지원.
13
+ * organization 필드 지원.
14
14
  */
15
15
  const openai_1 = tslib_1.__importDefault(require("openai"));
16
16
  const factory_js_1 = require("../factory.js");
@@ -19,10 +19,8 @@ const DEFAULT_MODEL = 'gpt-4o';
19
19
  const DEFAULT_MAX_TOKENS = 4096;
20
20
  class OpenAIAIClient {
21
21
  constructor(config) {
22
- // OpenAI SDK 는 apiKey 없을 시 OPENAI_API_KEY 환경변수 자동 인식.
23
- const apiKey = config.apiKey ?? process.env.OPENAI_API_KEY;
24
22
  this.sdk = new openai_1.default({
25
- apiKey,
23
+ apiKey: config.apiKey,
26
24
  ...(config.organization && { organization: config.organization }),
27
25
  ...(config.baseURL && { baseURL: config.baseURL })
28
26
  });
@@ -33,11 +31,13 @@ class OpenAIAIClient {
33
31
  const sdkMessages = buildSDKMessages(messages, options.systemPrompt);
34
32
  const response = await this.sdk.chat.completions.create({
35
33
  model: options.model ?? this.defaultModel,
36
- max_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,
34
+ /* `max_tokens` is refused by the gpt-5 and o-series models; every chat model takes this one. */
35
+ max_completion_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,
37
36
  ...(options.temperature !== undefined && { temperature: options.temperature }),
38
37
  ...(options.stopSequences && { stop: options.stopSequences }),
39
38
  messages: sdkMessages
40
39
  });
40
+ reportUsage(response, options, options.model ?? this.defaultModel);
41
41
  return response.choices[0]?.message?.content ?? '';
42
42
  }
43
43
  async generateJSON(prompt, options = {}) {
@@ -51,11 +51,13 @@ class OpenAIAIClient {
51
51
  const sdkMessages = buildSDKMessages(messages, enforcedSystem);
52
52
  const response = await this.sdk.chat.completions.create({
53
53
  model: options.model ?? this.defaultModel,
54
- max_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,
54
+ /* `max_tokens` is refused by the gpt-5 and o-series models; every chat model takes this one. */
55
+ max_completion_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,
55
56
  ...(options.temperature !== undefined && { temperature: options.temperature }),
56
57
  response_format: { type: 'json_object' },
57
58
  messages: sdkMessages
58
59
  });
60
+ reportUsage(response, options, options.model ?? this.defaultModel);
59
61
  const text = response.choices[0]?.message?.content ?? '{}';
60
62
  return (0, parse_json_js_1.parseJSONResponse)(text);
61
63
  }
@@ -81,7 +83,8 @@ class OpenAIAIClient {
81
83
  const toolChoice = buildOpenAIToolChoice(options);
82
84
  const response = await this.sdk.chat.completions.create({
83
85
  model: options.model ?? this.defaultModel,
84
- max_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,
86
+ /* `max_tokens` is refused by the gpt-5 and o-series models; every chat model takes this one. */
87
+ max_completion_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,
85
88
  ...(options.temperature !== undefined && { temperature: options.temperature }),
86
89
  ...(options.stopSequences && { stop: options.stopSequences }),
87
90
  messages: sdkMessages,
@@ -89,6 +92,7 @@ class OpenAIAIClient {
89
92
  ...(toolChoice !== undefined && { tool_choice: toolChoice }),
90
93
  ...(options.allowParallelToolCalls === false && { parallel_tool_calls: false })
91
94
  });
95
+ reportUsage(response, options, options.model ?? this.defaultModel);
92
96
  const choice = response.choices?.[0];
93
97
  const message = choice?.message;
94
98
  const text = message?.content || undefined;
@@ -113,8 +117,30 @@ class OpenAIAIClient {
113
117
  stopReason: normalizeOpenAIStopReason(choice?.finish_reason)
114
118
  };
115
119
  }
120
+ async listModels() {
121
+ const ids = [];
122
+ for await (const model of this.sdk.models.list())
123
+ ids.push(model.id);
124
+ return ids.filter(isOpenAIChatModel).sort();
125
+ }
116
126
  }
117
127
  exports.OpenAIAIClient = OpenAIAIClient;
128
+ /**
129
+ * OpenAI's model list has no capability field, so the conversation models are told apart by name: the GPT and
130
+ * o-series families, without their audio, realtime, speech, image, search and instruct variants. One rule, kept by a test.
131
+ */
132
+ function isOpenAIChatModel(id) {
133
+ if (!/^(gpt-|chatgpt-|o\d)/.test(id))
134
+ return false;
135
+ return !/(audio|realtime|tts|transcribe|image|search|instruct|embedding|moderation)/.test(id);
136
+ }
137
+ function reportUsage(response, options, requested) {
138
+ options.onUsage?.({
139
+ model: response?.model ?? requested,
140
+ inputTokens: response?.usage?.prompt_tokens,
141
+ outputTokens: response?.usage?.completion_tokens
142
+ });
143
+ }
118
144
  // ── 변환 유틸 ─────────────────────────────────────────────────────────
119
145
  function buildSDKMessages(messages, externalSystem) {
120
146
  const out = [];
@@ -1 +1 @@
1
- {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../server/service/providers/openai.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OpenAI AI Client adapter.\n *\n * 기본 모델: gpt-4o (구성에서 override 가능).\n * SDK: openai\n *\n * 비고: legacy `openai` config 키 (codelingua 의 사용처) 도 factory.ts 가 이 어댑터로 자동 매핑.\n * organization 필드 지원.\n */\nimport OpenAI from 'openai'\nimport type {\n AIChatOptions,\n AIClient,\n AIClientConfig,\n AIContentPart,\n AIJSONOptions,\n AIMessage,\n AITool,\n AIToolCall,\n AIToolChatOptions,\n AIToolChatResult,\n AIVisionOptions\n} from '../types.js'\nimport { registerAIProvider } from '../factory.js'\nimport { parseJSONResponse } from '../utils/parse-json.js'\n\nconst DEFAULT_MODEL = 'gpt-4o'\nconst DEFAULT_MAX_TOKENS = 4096\n\nexport class OpenAIAIClient implements AIClient {\n readonly id: string\n private sdk: OpenAI\n private defaultModel: string\n\n constructor(config: AIClientConfig) {\n // OpenAI SDK 는 apiKey 없을 시 OPENAI_API_KEY 환경변수 자동 인식.\n const apiKey = config.apiKey ?? process.env.OPENAI_API_KEY\n this.sdk = new OpenAI({\n apiKey,\n ...(config.organization && { organization: config.organization }),\n ...(config.baseURL && { baseURL: config.baseURL })\n })\n this.defaultModel = config.model ?? DEFAULT_MODEL\n this.id = `openai:${this.defaultModel}`\n }\n\n async chat(messages: AIMessage[], options: AIChatOptions = {}): Promise<string> {\n const sdkMessages = buildSDKMessages(messages, options.systemPrompt)\n const response = await this.sdk.chat.completions.create({\n model: options.model ?? this.defaultModel,\n max_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(options.stopSequences && { stop: options.stopSequences }),\n messages: sdkMessages as any\n })\n return response.choices[0]?.message?.content ?? ''\n }\n\n async generateJSON<T = unknown>(\n prompt: string | AIMessage[],\n options: AIJSONOptions = {}\n ): Promise<T> {\n const messages: AIMessage[] =\n typeof prompt === 'string' ? [{ role: 'user', content: prompt }] : prompt\n\n const schemaHint = options.schema\n ? `\\n\\nResponse MUST conform to this JSON schema:\\n${JSON.stringify(options.schema)}`\n : ''\n const enforcedSystem =\n (options.systemPrompt ? options.systemPrompt + '\\n\\n' : '') +\n 'Return only valid JSON. No markdown code fences, no commentary.' +\n schemaHint\n\n const sdkMessages = buildSDKMessages(messages, enforcedSystem)\n const response = await this.sdk.chat.completions.create({\n model: options.model ?? this.defaultModel,\n max_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n response_format: { type: 'json_object' },\n messages: sdkMessages as any\n })\n const text = response.choices[0]?.message?.content ?? '{}'\n return parseJSONResponse<T>(text)\n }\n\n async vision(\n image: Buffer | Uint8Array | { url: string },\n prompt: string,\n options: AIVisionOptions = {}\n ): Promise<string> {\n const imagePart: AIContentPart =\n typeof image === 'object' && 'url' in image\n ? { type: 'image', source: { kind: 'url', url: image.url } }\n : {\n type: 'image',\n source: { kind: 'base64', data: image as Buffer | Uint8Array, mediaType: 'image/png' }\n }\n\n return this.chat(\n [{ role: 'user', content: [imagePart, { type: 'text', text: prompt }] }],\n options\n )\n }\n\n async chatWithTools(\n messages: AIMessage[],\n tools: AITool[],\n options: AIToolChatOptions = {}\n ): Promise<AIToolChatResult> {\n const sdkMessages = buildSDKMessages(messages, options.systemPrompt)\n\n const sdkTools = tools.map(t => ({\n type: 'function' as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.parameters\n }\n }))\n\n const toolChoice = buildOpenAIToolChoice(options)\n\n const response = await this.sdk.chat.completions.create({\n model: options.model ?? this.defaultModel,\n max_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(options.stopSequences && { stop: options.stopSequences }),\n messages: sdkMessages as any,\n tools: sdkTools,\n ...(toolChoice !== undefined && { tool_choice: toolChoice as any }),\n ...(options.allowParallelToolCalls === false && { parallel_tool_calls: false })\n })\n\n const choice = response.choices?.[0]\n const message = choice?.message\n const text = message?.content || undefined\n\n const toolCalls: AIToolCall[] = (message?.tool_calls || []).map((tc: any) => {\n const fn = tc.function\n let args: Record<string, any> = {}\n try {\n args = fn?.arguments ? JSON.parse(fn.arguments) : {}\n } catch {\n args = {}\n }\n return {\n id: typeof tc.id === 'string' && tc.id ? tc.id : `call_${Math.random().toString(36).slice(2, 12)}`,\n name: fn?.name ?? '',\n arguments: args\n }\n })\n\n return {\n text,\n toolCalls,\n stopReason: normalizeOpenAIStopReason(choice?.finish_reason)\n }\n }\n}\n\n// ── 변환 유틸 ─────────────────────────────────────────────────────────\n\nfunction buildSDKMessages(messages: AIMessage[], externalSystem?: string): any[] {\n const out: any[] = []\n\n const systemFromMessages = messages\n .filter(m => m.role === 'system')\n .map(extractTextContent)\n .filter(Boolean)\n .join('\\n\\n')\n const combinedSystem = [externalSystem, systemFromMessages].filter(Boolean).join('\\n\\n')\n if (combinedSystem) {\n out.push({ role: 'system', content: combinedSystem })\n }\n\n for (const msg of messages) {\n if (msg.role === 'system') continue\n const sdkMsgs = toSDKMessages(msg)\n out.push(...sdkMsgs)\n }\n\n return out\n}\n\n/**\n * 한 AIMessage 가 다중 tool_result 를 가지면 OpenAI 측에서 여러 role:'tool' 메시지로\n * 분리해야 함 (OpenAI 는 메시지당 tool_call_id 하나만 매핑). 따라서 array 를 반환.\n */\nexport function toSDKMessages(msg: AIMessage): any[] {\n if (typeof msg.content === 'string') {\n return [{ role: msg.role, content: msg.content }]\n }\n\n const parts = msg.content\n const toolUseParts = parts.filter(p => p.type === 'tool_use') as any[]\n const toolResultParts = parts.filter(p => p.type === 'tool_result') as any[]\n const otherParts = parts.filter(p => p.type !== 'tool_use' && p.type !== 'tool_result')\n\n // tool_result 가 있으면 각각을 role='tool' 메시지로 분리 (OpenAI 요구사항).\n if (toolResultParts.length > 0) {\n const results = toolResultParts.map((r: any) => ({\n role: 'tool',\n tool_call_id: r.toolUseId,\n content: r.content\n }))\n /* A tool message holds text only - pictures a read tool returned follow as one user message (agentic-loop takeToolImages). */\n const images = otherParts.filter(p => p.type === 'image')\n return images.length ? [...results, { role: 'user', content: images.map(toSDKContentPart) }] : results\n }\n\n // assistant 측 tool_use 가 있으면 message.tool_calls 로\n const out: any = { role: msg.role }\n if (otherParts.length > 0) {\n out.content = otherParts.map(toSDKContentPart)\n } else {\n out.content = null\n }\n if (toolUseParts.length > 0) {\n out.tool_calls = toolUseParts.map(p => ({\n id: p.id,\n type: 'function',\n function: { name: p.name, arguments: JSON.stringify(p.arguments) }\n }))\n }\n return [out]\n}\n\nfunction toSDKContentPart(part: AIContentPart): any {\n if (part.type === 'text') {\n return { type: 'text', text: part.text }\n }\n if (part.type === 'tool_use' || part.type === 'tool_result') {\n // 처리는 toSDKMessage 가 함 — 도달하면 버그.\n return { type: 'text', text: '' }\n }\n // image\n if (part.source.kind === 'url') {\n return { type: 'image_url', image_url: { url: part.source.url } }\n }\n const b64 = Buffer.isBuffer(part.source.data)\n ? part.source.data.toString('base64')\n : Buffer.from(part.source.data).toString('base64')\n return {\n type: 'image_url',\n image_url: { url: `data:${part.source.mediaType};base64,${b64}` }\n }\n}\n\nfunction extractTextContent(msg: AIMessage): string {\n if (typeof msg.content === 'string') return msg.content\n return msg.content\n .filter(p => p.type === 'text')\n .map(p => (p as { type: 'text'; text: string }).text)\n .join('\\n')\n}\n\nfunction buildOpenAIToolChoice(options: AIToolChatOptions): any {\n const choice = options.toolChoice ?? 'auto'\n if (choice === 'none') return 'none'\n if (choice === 'required') {\n if (options.forceTool) {\n return { type: 'function', function: { name: options.forceTool } }\n }\n return 'required'\n }\n return 'auto'\n}\n\nfunction normalizeOpenAIStopReason(raw: any): AIToolChatResult['stopReason'] {\n switch (raw) {\n case 'tool_calls':\n return 'tool_use'\n case 'function_call':\n return 'tool_use'\n case 'stop':\n return 'end_turn'\n case 'length':\n return 'max_tokens'\n case 'content_filter':\n return 'stop_sequence'\n default:\n return raw ?? 'end_turn'\n }\n}\n\n// Auto-register at module load\nregisterAIProvider('openai', config => new OpenAIAIClient(config))\n"]}
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../server/service/providers/openai.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * OpenAI AI Client adapter.\n *\n * 기본 모델: gpt-4o (구성에서 override 가능).\n * SDK: openai\n *\n * organization 필드 지원.\n */\nimport OpenAI from 'openai'\nimport type {\n AIChatOptions,\n AIClient,\n AIClientConfig,\n AIContentPart,\n AIJSONOptions,\n AIMessage,\n AITool,\n AIToolCall,\n AIToolChatOptions,\n AIToolChatResult,\n AIVisionOptions\n} from '../types.js'\nimport { registerAIProvider } from '../factory.js'\nimport { parseJSONResponse } from '../utils/parse-json.js'\n\nconst DEFAULT_MODEL = 'gpt-4o'\nconst DEFAULT_MAX_TOKENS = 4096\n\nexport class OpenAIAIClient implements AIClient {\n readonly id: string\n private sdk: OpenAI\n private defaultModel: string\n\n constructor(config: AIClientConfig) {\n this.sdk = new OpenAI({\n apiKey: config.apiKey,\n ...(config.organization && { organization: config.organization }),\n ...(config.baseURL && { baseURL: config.baseURL })\n })\n this.defaultModel = config.model ?? DEFAULT_MODEL\n this.id = `openai:${this.defaultModel}`\n }\n\n async chat(messages: AIMessage[], options: AIChatOptions = {}): Promise<string> {\n const sdkMessages = buildSDKMessages(messages, options.systemPrompt)\n const response = await this.sdk.chat.completions.create({\n model: options.model ?? this.defaultModel,\n /* `max_tokens` is refused by the gpt-5 and o-series models; every chat model takes this one. */\n max_completion_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(options.stopSequences && { stop: options.stopSequences }),\n messages: sdkMessages as any\n })\n reportUsage(response, options, options.model ?? this.defaultModel)\n return response.choices[0]?.message?.content ?? ''\n }\n\n async generateJSON<T = unknown>(\n prompt: string | AIMessage[],\n options: AIJSONOptions = {}\n ): Promise<T> {\n const messages: AIMessage[] =\n typeof prompt === 'string' ? [{ role: 'user', content: prompt }] : prompt\n\n const schemaHint = options.schema\n ? `\\n\\nResponse MUST conform to this JSON schema:\\n${JSON.stringify(options.schema)}`\n : ''\n const enforcedSystem =\n (options.systemPrompt ? options.systemPrompt + '\\n\\n' : '') +\n 'Return only valid JSON. No markdown code fences, no commentary.' +\n schemaHint\n\n const sdkMessages = buildSDKMessages(messages, enforcedSystem)\n const response = await this.sdk.chat.completions.create({\n model: options.model ?? this.defaultModel,\n /* `max_tokens` is refused by the gpt-5 and o-series models; every chat model takes this one. */\n max_completion_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n response_format: { type: 'json_object' },\n messages: sdkMessages as any\n })\n reportUsage(response, options, options.model ?? this.defaultModel)\n const text = response.choices[0]?.message?.content ?? '{}'\n return parseJSONResponse<T>(text)\n }\n\n async vision(\n image: Buffer | Uint8Array | { url: string },\n prompt: string,\n options: AIVisionOptions = {}\n ): Promise<string> {\n const imagePart: AIContentPart =\n typeof image === 'object' && 'url' in image\n ? { type: 'image', source: { kind: 'url', url: image.url } }\n : {\n type: 'image',\n source: { kind: 'base64', data: image as Buffer | Uint8Array, mediaType: 'image/png' }\n }\n\n return this.chat(\n [{ role: 'user', content: [imagePart, { type: 'text', text: prompt }] }],\n options\n )\n }\n\n async chatWithTools(\n messages: AIMessage[],\n tools: AITool[],\n options: AIToolChatOptions = {}\n ): Promise<AIToolChatResult> {\n const sdkMessages = buildSDKMessages(messages, options.systemPrompt)\n\n const sdkTools = tools.map(t => ({\n type: 'function' as const,\n function: {\n name: t.name,\n description: t.description,\n parameters: t.parameters\n }\n }))\n\n const toolChoice = buildOpenAIToolChoice(options)\n\n const response = await this.sdk.chat.completions.create({\n model: options.model ?? this.defaultModel,\n /* `max_tokens` is refused by the gpt-5 and o-series models; every chat model takes this one. */\n max_completion_tokens: options.maxTokens ?? DEFAULT_MAX_TOKENS,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n ...(options.stopSequences && { stop: options.stopSequences }),\n messages: sdkMessages as any,\n tools: sdkTools,\n ...(toolChoice !== undefined && { tool_choice: toolChoice as any }),\n ...(options.allowParallelToolCalls === false && { parallel_tool_calls: false })\n })\n reportUsage(response, options, options.model ?? this.defaultModel)\n\n const choice = response.choices?.[0]\n const message = choice?.message\n const text = message?.content || undefined\n\n const toolCalls: AIToolCall[] = (message?.tool_calls || []).map((tc: any) => {\n const fn = tc.function\n let args: Record<string, any> = {}\n try {\n args = fn?.arguments ? JSON.parse(fn.arguments) : {}\n } catch {\n args = {}\n }\n return {\n id: typeof tc.id === 'string' && tc.id ? tc.id : `call_${Math.random().toString(36).slice(2, 12)}`,\n name: fn?.name ?? '',\n arguments: args\n }\n })\n\n return {\n text,\n toolCalls,\n stopReason: normalizeOpenAIStopReason(choice?.finish_reason)\n }\n }\n\n async listModels(): Promise<string[]> {\n const ids: string[] = []\n for await (const model of this.sdk.models.list()) ids.push(model.id)\n return ids.filter(isOpenAIChatModel).sort()\n }\n}\n\n/**\n * OpenAI's model list has no capability field, so the conversation models are told apart by name: the GPT and\n * o-series families, without their audio, realtime, speech, image, search and instruct variants. One rule, kept by a test.\n */\nexport function isOpenAIChatModel(id: string): boolean {\n if (!/^(gpt-|chatgpt-|o\\d)/.test(id)) return false\n return !/(audio|realtime|tts|transcribe|image|search|instruct|embedding|moderation)/.test(id)\n}\n\nfunction reportUsage(response: any, options: AIChatOptions, requested: string): void {\n options.onUsage?.({\n model: response?.model ?? requested,\n inputTokens: response?.usage?.prompt_tokens,\n outputTokens: response?.usage?.completion_tokens\n })\n}\n\n// ── 변환 유틸 ─────────────────────────────────────────────────────────\n\nfunction buildSDKMessages(messages: AIMessage[], externalSystem?: string): any[] {\n const out: any[] = []\n\n const systemFromMessages = messages\n .filter(m => m.role === 'system')\n .map(extractTextContent)\n .filter(Boolean)\n .join('\\n\\n')\n const combinedSystem = [externalSystem, systemFromMessages].filter(Boolean).join('\\n\\n')\n if (combinedSystem) {\n out.push({ role: 'system', content: combinedSystem })\n }\n\n for (const msg of messages) {\n if (msg.role === 'system') continue\n const sdkMsgs = toSDKMessages(msg)\n out.push(...sdkMsgs)\n }\n\n return out\n}\n\n/**\n * 한 AIMessage 가 다중 tool_result 를 가지면 OpenAI 측에서 여러 role:'tool' 메시지로\n * 분리해야 함 (OpenAI 는 메시지당 tool_call_id 하나만 매핑). 따라서 array 를 반환.\n */\nexport function toSDKMessages(msg: AIMessage): any[] {\n if (typeof msg.content === 'string') {\n return [{ role: msg.role, content: msg.content }]\n }\n\n const parts = msg.content\n const toolUseParts = parts.filter(p => p.type === 'tool_use') as any[]\n const toolResultParts = parts.filter(p => p.type === 'tool_result') as any[]\n const otherParts = parts.filter(p => p.type !== 'tool_use' && p.type !== 'tool_result')\n\n // tool_result 가 있으면 각각을 role='tool' 메시지로 분리 (OpenAI 요구사항).\n if (toolResultParts.length > 0) {\n const results = toolResultParts.map((r: any) => ({\n role: 'tool',\n tool_call_id: r.toolUseId,\n content: r.content\n }))\n /* A tool message holds text only - pictures a read tool returned follow as one user message (agentic-loop takeToolImages). */\n const images = otherParts.filter(p => p.type === 'image')\n return images.length ? [...results, { role: 'user', content: images.map(toSDKContentPart) }] : results\n }\n\n // assistant 측 tool_use 가 있으면 message.tool_calls 로\n const out: any = { role: msg.role }\n if (otherParts.length > 0) {\n out.content = otherParts.map(toSDKContentPart)\n } else {\n out.content = null\n }\n if (toolUseParts.length > 0) {\n out.tool_calls = toolUseParts.map(p => ({\n id: p.id,\n type: 'function',\n function: { name: p.name, arguments: JSON.stringify(p.arguments) }\n }))\n }\n return [out]\n}\n\nfunction toSDKContentPart(part: AIContentPart): any {\n if (part.type === 'text') {\n return { type: 'text', text: part.text }\n }\n if (part.type === 'tool_use' || part.type === 'tool_result') {\n // 처리는 toSDKMessage 가 함 — 도달하면 버그.\n return { type: 'text', text: '' }\n }\n // image\n if (part.source.kind === 'url') {\n return { type: 'image_url', image_url: { url: part.source.url } }\n }\n const b64 = Buffer.isBuffer(part.source.data)\n ? part.source.data.toString('base64')\n : Buffer.from(part.source.data).toString('base64')\n return {\n type: 'image_url',\n image_url: { url: `data:${part.source.mediaType};base64,${b64}` }\n }\n}\n\nfunction extractTextContent(msg: AIMessage): string {\n if (typeof msg.content === 'string') return msg.content\n return msg.content\n .filter(p => p.type === 'text')\n .map(p => (p as { type: 'text'; text: string }).text)\n .join('\\n')\n}\n\nfunction buildOpenAIToolChoice(options: AIToolChatOptions): any {\n const choice = options.toolChoice ?? 'auto'\n if (choice === 'none') return 'none'\n if (choice === 'required') {\n if (options.forceTool) {\n return { type: 'function', function: { name: options.forceTool } }\n }\n return 'required'\n }\n return 'auto'\n}\n\nfunction normalizeOpenAIStopReason(raw: any): AIToolChatResult['stopReason'] {\n switch (raw) {\n case 'tool_calls':\n return 'tool_use'\n case 'function_call':\n return 'tool_use'\n case 'stop':\n return 'end_turn'\n case 'length':\n return 'max_tokens'\n case 'content_filter':\n return 'stop_sequence'\n default:\n return raw ?? 'end_turn'\n }\n}\n\n// Auto-register at module load\nregisterAIProvider('openai', config => new OpenAIAIClient(config))\n"]}
@@ -76,6 +76,17 @@ export interface AIChatOptions {
76
76
  */
77
77
  systemPrompt?: string;
78
78
  stopSequences?: string[];
79
+ /**
80
+ * Told what one provider call used, right after it returns. The metering boundary (aiClientFor) sets this per
81
+ * call so the usage row knows whose call it was; adapters call it once for every request they send.
82
+ */
83
+ onUsage?: (usage: AIProviderUsage) => void;
84
+ }
85
+ /** What one provider request used, as the provider reported it. A count the provider did not report stays undefined. */
86
+ export interface AIProviderUsage {
87
+ model: string;
88
+ inputTokens?: number;
89
+ outputTokens?: number;
79
90
  }
80
91
  export interface AIJSONOptions extends AIChatOptions {
81
92
  /** JSON Schema (옵션). 어댑터가 지원하면 structured output 강제 */
@@ -156,13 +167,16 @@ export interface AIClient {
156
167
  * 어댑터별로 Anthropic tool_use / OpenAI tools / Gemini functionCalls 로 매핑.
157
168
  */
158
169
  chatWithTools?(messages: AIMessage[], tools: AITool[], options?: AIToolChatOptions): Promise<AIToolChatResult>;
170
+ /** The ids of the conversation models this key may call, sorted. Reading it also proves the key is accepted. */
171
+ listModels(): Promise<string[]>;
159
172
  }
160
173
  export interface AIClientConfig {
161
174
  /** 등록된 provider 이름 (예: 'anthropic', 'openai', 'gemini') */
162
175
  provider: string;
163
- /** 기본 모델 ID */
176
+ /** The model every call uses unless a call names another. */
164
177
  model?: string;
165
- apiKey?: string;
178
+ /** Required - a provider is never reached with a key from the process environment. */
179
+ apiKey: string;
166
180
  /** OpenAI organization (provider:'openai' 전용) */
167
181
  organization?: string;
168
182
  baseURL?: string;