@things-factory/ai-client-base 10.1.168 → 10.1.170
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/service/agentic-loop/agentic-loop.d.ts +9 -0
- package/dist-server/service/agentic-loop/agentic-loop.js +22 -3
- package/dist-server/service/agentic-loop/agentic-loop.js.map +1 -1
- package/dist-server/service/assistant/open-subject.d.ts +4 -0
- package/dist-server/service/assistant/open-subject.js +3 -0
- package/dist-server/service/assistant/open-subject.js.map +1 -1
- package/dist-server/service/assistant/registry-assistant.js +16 -2
- package/dist-server/service/assistant/registry-assistant.js.map +1 -1
- package/dist-server/service/providers/anthropic.d.ts +2 -0
- package/dist-server/service/providers/anthropic.js +7 -3
- package/dist-server/service/providers/anthropic.js.map +1 -1
- package/dist-server/service/providers/gemini.js +1 -1
- package/dist-server/service/providers/gemini.js.map +1 -1
- package/dist-server/service/providers/openai.js +1 -1
- package/dist-server/service/providers/openai.js.map +1 -1
- package/dist-server/service/tool-registry/call-recorder.d.ts +16 -0
- package/dist-server/service/tool-registry/call-recorder.js +24 -0
- package/dist-server/service/tool-registry/call-recorder.js.map +1 -0
- package/dist-server/service/tool-registry/index.d.ts +2 -0
- package/dist-server/service/tool-registry/index.js +5 -1
- package/dist-server/service/tool-registry/index.js.map +1 -1
- package/dist-server/service/utils/parse-json.d.ts +12 -1
- package/dist-server/service/utils/parse-json.js +18 -4
- package/dist-server/service/utils/parse-json.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/server/service/agentic-loop/agentic-loop.test.ts +76 -0
- package/server/service/agentic-loop/agentic-loop.ts +22 -3
- package/server/service/assistant/open-subject.ts +3 -0
- package/server/service/assistant/registry-assistant.test.ts +63 -1
- package/server/service/assistant/registry-assistant.ts +15 -2
- package/server/service/providers/anthropic.ts +8 -4
- package/server/service/providers/gemini.ts +1 -1
- package/server/service/providers/json-answer.test.ts +53 -0
- package/server/service/providers/openai.ts +1 -1
- package/server/service/tool-registry/call-recorder.ts +47 -0
- package/server/service/tool-registry/index.ts +3 -0
- package/server/service/utils/parse-json.ts +21 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../../server/service/providers/anthropic.ts"],"names":[],"mappings":";;;;AAAA;;;;;GAKG;AACH,oEAAyC;AAczC,8CAAkD;AAClD,0DAA0D;AAE1D,MAAM,aAAa,GAAG,mBAAmB,CAAA;AACzC,MAAM,kBAAkB,GAAG,IAAI,CAAA;AAE/B,MAAa,iBAAiB;IAK5B,YAAY,MAAsB;QAChC,IAAI,CAAC,GAAG,GAAG,IAAI,aAAS,CAAC;YACvB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;SACnD,CAAC,CAAA;QACF,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,KAAK,IAAI,aAAa,CAAA;QACjD,IAAI,CAAC,EAAE,GAAG,aAAa,IAAI,CAAC,YAAY,EAAE,CAAA;IAC5C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAqB,EAAE,UAAyB,EAAE;QAC3D,MAAM,kBAAkB,GAAG,QAAQ;aAChC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;aAChC,GAAG,CAAC,kBAAkB,CAAC;aACvB,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,MAAM,CAAC,CAAA;QACf,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAEtF,MAAM,YAAY,GAAG,QAAQ;aAC1B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;aAChC,GAAG,CAAC,kBAAkB,CAAC,CAAA;QAE1B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC9C,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY;YACzC,UAAU,EAAE,OAAO,CAAC,SAAS,IAAI,kBAAkB;YACnD,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;YAC9E,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC;YACzB,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;YACvE,QAAQ,EAAE,YAAY;SAChB,CAAC,CAAA;QACT,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,CAAA;QAElE,OAAO,mBAAmB,CAAC,QAAQ,CAAC,CAAA;IACtC,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,MAA4B,EAC5B,UAAyB,EAAE;QAE3B,MAAM,QAAQ,GACZ,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;QAE3E,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM;YAC/B,CAAC,CAAC,mDAAmD,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACrF,CAAC,CAAC,EAAE,CAAA;QACN,MAAM,cAAc,GAClB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3D,iEAAiE;YACjE,UAAU,CAAA;QAEZ,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,CAAC,CAAA;QACpF,OAAO,IAAA,iCAAiB,EAAI,IAAI,CAAC,CAAA;IACnC,CAAC;IAED,KAAK,CAAC,MAAM,CACV,KAA4C,EAC5C,MAAc,EACd,UAA2B,EAAE;QAE7B,MAAM,SAAS,GACb,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,KAAK;YACzC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE;YAC5D,CAAC,CAAC;gBACE,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAA4B,EAAE,SAAS,EAAE,WAAW,EAAE;aACvF,CAAA;QAEP,OAAO,IAAI,CAAC,IAAI,CACd,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EACxE,OAAO,CACR,CAAA;IACH,CAAC;IAED,KAAK,CAAC,aAAa,CACjB,QAAqB,EACrB,KAAe,EACf,UAA6B,EAAE;QAE/B,MAAM,kBAAkB,GAAG,QAAQ;aAChC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;aAChC,GAAG,CAAC,kBAAkB,CAAC;aACvB,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,MAAM,CAAC,CAAA;QACf,MAAM,MAAM,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,kBAAkB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACtF,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;QAEtF,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC/B,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,YAAY,EAAE,CAAC,CAAC,UAAiB;SAClC,CAAC,CAAC,CAAA;QAEH,MAAM,UAAU,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAA;QAEpD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC;YAC9C,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY;YACzC,UAAU,EAAE,OAAO,CAAC,SAAS,IAAI,kBAAkB;YACnD,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;YAC9E,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC;YACzB,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,cAAc,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;YACvE,QAAQ,EAAE,YAAY;YACtB,KAAK,EAAE,QAAQ;YACf,GAAG,CAAC,UAAU,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;SACxC,CAAC,CAAA;QACT,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,CAAA;QAElE,MAAM,IAAI,GAAG,mBAAmB,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAA;QACvD,MAAM,SAAS,GAAiB,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;YAC7D,CAAC,CAAC,QAAQ,CAAC,OAAO;iBACb,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC;iBAC9C,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;gBAChB,EAAE,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;gBAC/F,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,SAAS,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAwB;aAC1F,CAAC,CAAC;YACP,CAAC,CAAC,EAAE,CAAA;QAEN,OAAO;YACL,IAAI;YACJ,SAAS;YACT,UAAU,EAAE,4BAA4B,CAAC,QAAQ,CAAC,WAAW,CAAC;SAC/D,CAAA;IACH,CAAC;IAED,2DAA2D;IAC3D,KAAK,CAAC,UAAU;QACd,MAAM,GAAG,GAAa,EAAE,CAAA;QACxB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;YAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACpE,OAAO,GAAG,CAAC,IAAI,EAAE,CAAA;IACnB,CAAC;CACF;AAtID,8CAsIC;AAED,SAAS,WAAW,CAAC,QAAa,EAAE,OAAsB,EAAE,SAAiB;IAC3E,OAAO,CAAC,OAAO,EAAE,CAAC;QAChB,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,SAAS;QACnC,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY;QAC1C,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa;KAC7C,CAAC,CAAA;AACJ,CAAC;AAED,qEAAqE;AAErE,SAAS,kBAAkB,CAAC,GAAc;IACxC,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,OAAO,CAAA;IACvD,OAAO,GAAG,CAAC,OAAO;SACf,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;SAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAE,CAAoC,CAAC,IAAI,CAAC;SACpD,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAc;IACxC,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAA;IACjD,CAAC;IACD,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC;KAClD,CAAA;AACH,CAAC;AAED,SAAS,uBAAuB,CAAC,IAAmB;IAClD,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACzB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAA;IAC1C,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7B,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,CAAA;IAClF,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAChC,OAAO;YACL,IAAI,EAAE,aAAa;YACnB,WAAW,EAAE,IAAI,CAAC,SAAS;YAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;SACxC,CAAA;IACH,CAAC;IACD,QAAQ;IACR,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC/B,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,CAAA;IACzE,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;QAC5C,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACrC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACpD,OAAO;QACL,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE;KACpE,CAAA;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,QAAa;IACxC,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAA;IAC5D,OAAO,QAAQ,CAAC,OAAO;SACpB,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;SAC1C,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;SAC7B,IAAI,CAAC,EAAE,CAAC,CAAA;AACb,CAAC;AAED,SAAS,wBAAwB,CAAC,OAA0B;IAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAA;IAC3C,MAAM,eAAe,GAAG,OAAO,CAAC,sBAAsB,KAAK,KAAK,CAAA;IAChE,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;IACzB,CAAC;IACD,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QAC1B,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,OAAO;gBACL,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,OAAO,CAAC,SAAS;gBACvB,GAAG,CAAC,eAAe,IAAI,EAAE,yBAAyB,EAAE,IAAI,EAAE,CAAC;aAC5D,CAAA;QACH,CAAC;QACD,OAAO;YACL,IAAI,EAAE,KAAK;YACX,GAAG,CAAC,eAAe,IAAI,EAAE,yBAAyB,EAAE,IAAI,EAAE,CAAC;SAC5D,CAAA;IACH,CAAC;IACD,SAAS;IACT,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,GAAG,CAAC,eAAe,IAAI,EAAE,yBAAyB,EAAE,IAAI,EAAE,CAAC;KAC5D,CAAA;AACH,CAAC;AAED,SAAS,4BAA4B,CAAC,GAAQ;IAC5C,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,UAAU;YACb,OAAO,UAAU,CAAA;QACnB,KAAK,UAAU;YACb,OAAO,UAAU,CAAA;QACnB,KAAK,YAAY;YACf,OAAO,YAAY,CAAA;QACrB,KAAK,eAAe;YAClB,OAAO,eAAe,CAAA;QACxB;YACE,OAAO,GAAG,IAAI,UAAU,CAAA;IAC5B,CAAC;AACH,CAAC;AAED,+BAA+B;AAC/B,IAAA,+BAAkB,EAAC,WAAW,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAA","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"]}
|
|
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 return extractResponseText(await this.send(messages, options))\n }\n\n /** One request, the provider's response as it came: `generateJSON` reads whether it stopped at the output limit. */\n private async send(messages: AIMessage[], options: AIChatOptions): Promise<any> {\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 return 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 response = await this.send(messages, { ...options, systemPrompt: enforcedSystem })\n return parseJSONResponse<T>(extractResponseText(response), { cutShort: response?.stop_reason === 'max_tokens' })\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"]}
|
|
@@ -48,7 +48,7 @@ class GeminiAIClient {
|
|
|
48
48
|
const result = await model.generateContent({ contents });
|
|
49
49
|
reportUsage(result, options, options.model ?? this.defaultModel);
|
|
50
50
|
const text = result.response.text();
|
|
51
|
-
return (0, parse_json_js_1.parseJSONResponse)(text);
|
|
51
|
+
return (0, parse_json_js_1.parseJSONResponse)(text, { cutShort: result.response.candidates?.[0]?.finishReason === 'MAX_TOKENS' });
|
|
52
52
|
}
|
|
53
53
|
async vision(image, prompt, options = {}) {
|
|
54
54
|
const imagePart = typeof image === 'object' && 'url' in image
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../../server/service/providers/gemini.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;GAQG;AACH,yDAA0D;AAc1D,8CAAkD;AAClD,0DAA0D;AAE1D,iDAAiD;AACjD,gDAAgD;AAChD,4EAA4E;AAC5E,MAAM,aAAa,GAAG,wBAAwB,CAAA;AAC9C,MAAM,kBAAkB,GAAG,IAAI,CAAA;AAE/B,MAAa,cAAc;IAOzB,YAAY,MAAsB;QAChC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAA;QAC3B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAA;QAC7B,IAAI,CAAC,GAAG,GAAG,IAAI,kCAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAChD,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,KAAK,IAAI,aAAa,CAAA;QACjD,IAAI,CAAC,EAAE,GAAG,UAAU,IAAI,CAAC,YAAY,EAAE,CAAA;IACzC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAqB,EAAE,UAAyB,EAAE;QAC3D,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;QAChF,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAA;QACpD,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAS,CAAC,CAAA;QAC/D,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,CAAA;QAChE,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;IAC/B,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,MAA4B,EAC5B,UAAyB,EAAE;QAE3B,MAAM,QAAQ,GACZ,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;QAC3E,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;QAEhF,MAAM,gBAAgB,GAAQ,oBAAoB,CAAC,OAAO,CAAC,CAAA;QAC3D,gBAAgB,CAAC,gBAAgB,GAAG,kBAAkB,CAAA;QACtD,IAAI,OAAO,CAAC,MAAM;YAAE,gBAAgB,CAAC,cAAc,GAAG,OAAO,CAAC,MAAM,CAAA;QAEpE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC;YACxC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY;YACzC,GAAG,CAAC,YAAY,IAAI,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAC;YACxD,gBAAgB;SACjB,EAAE,IAAI,CAAC,cAAc,CAAC,CAAA;QACvB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAS,CAAC,CAAA;QAC/D,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,CAAA;QAChE,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAA;QACnC,OAAO,IAAA,iCAAiB,EAAI,IAAI,CAAC,CAAA;IACnC,CAAC;IAED,KAAK,CAAC,MAAM,CACV,KAA4C,EAC5C,MAAc,EACd,UAA2B,EAAE;QAE7B,MAAM,SAAS,GACb,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,KAAK;YACzC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE;YAC5D,CAAC,CAAC;gBACE,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAA4B,EAAE,SAAS,EAAE,WAAW,EAAE;aACvF,CAAA;QAEP,OAAO,IAAI,CAAC,IAAI,CACd,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EACxE,OAAO,CACR,CAAA;IACH,CAAC;IAEO,UAAU,CAAC,OAAsB,EAAE,iBAA0B;QACnE,OAAO,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC;YACjC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY;YACzC,GAAG,CAAC,iBAAiB,IAAI,EAAE,iBAAiB,EAAE,CAAC;YAC/C,gBAAgB,EAAE,oBAAoB,CAAC,OAAO,CAAC;SAChD,EAAE,IAAI,CAAC,cAAc,CAAC,CAAA;IACzB,CAAC;IAED,KAAK,CAAC,aAAa,CACjB,QAAqB,EACrB,KAAe,EACf,UAA6B,EAAE;QAE/B,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;QAEhF,yDAAyD;QACzD,sDAAsD;QACtD,MAAM,oBAAoB,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC3C,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,UAAU,EAAE,uBAAuB,CAAC,CAAC,CAAC,UAAU,CAAC;SAClD,CAAC,CAAC,CAAA;QAEH,MAAM,UAAU,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAA;QAEjD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC;YACxC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY;YACzC,GAAG,CAAC,YAAY,IAAI,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAC;YACxD,gBAAgB,EAAE,oBAAoB,CAAC,OAAO,CAAC;YAC/C,KAAK,EAAE,CAAC,EAAE,oBAAoB,EAAE,CAAQ;YACxC,GAAG,CAAC,UAAU,IAAI,EAAE,UAAU,EAAE,CAAC;SAC3B,EAAE,IAAI,CAAC,cAAc,CAAC,CAAA;QAE9B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAS,CAAC,CAAA;QAC/D,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,CAAA;QAChE,MAAM,QAAQ,GAAQ,MAAM,CAAC,QAAQ,CAAA;QAErC,MAAM,SAAS,GAAG,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,CAAA;QAC3C,MAAM,KAAK,GAAG,SAAS,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE,CAAA;QAE7C,2DAA2D;QAC3D,0DAA0D;QAC1D,qCAAqC;QACrC,MAAM,SAAS,GAAG,KAAK;aACpB,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;aACtE,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QAC1B,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,SAAS,CAAA;QAE5C,0DAA0D;QAC1D,qEAAqE;QACrE,6DAA6D;QAC7D,EAAE;QACF,wEAAwE;QACxE,iFAAiF;QACjF,IAAI,aAAa,GAAG,CAAC,CAAA;QACrB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACrB,MAAM,SAAS,GAAiB,KAAK;aAClC,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,YAAY,IAAI,OAAO,CAAC,CAAC,YAAY,CAAC,IAAI,KAAK,QAAQ,CAAC;aAC9E,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE;YACd,uDAAuD;YACvD,MAAM,GAAG,GAAuB,CAAC,CAAC,gBAAgB,IAAI,CAAC,CAAC,iBAAiB,CAAA;YACzE,OAAO;gBACL,EAAE,EAAE,KAAK,CAAC,CAAC,YAAY,CAAC,IAAI,IAAI,EAAE,IAAI,aAAa,EAAE,EAAE;gBACvD,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,IAAI;gBACzB,SAAS,EACP,CAAC,CAAC,YAAY,CAAC,IAAI,IAAI,OAAO,CAAC,CAAC,YAAY,CAAC,IAAI,KAAK,QAAQ;oBAC5D,CAAC,CAAE,CAAC,CAAC,YAAY,CAAC,IAA4B;oBAC9C,CAAC,CAAC,EAAE;gBACR,GAAG,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI;oBAC/C,YAAY,EAAE,EAAE,gBAAgB,EAAE,GAAG,EAAE;iBACxC,CAAC;aACH,CAAA;QACH,CAAC,CAAC,CAAA;QAEJ,OAAO;YACL,IAAI;YACJ,SAAS;YACT,UAAU,EAAE,yBAAyB,CAAC,SAAS,EAAE,YAAY,EAAE,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;SACrF,CAAA;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,2CAA2C,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;QAC9F,MAAM,GAAG,GAAa,EAAE,CAAA;QACxB,IAAI,SAAS,GAAG,EAAE,CAAA;QAClB,GAAG,CAAC;YACF,MAAM,GAAG,GAAG,GAAG,IAAI,+BAA+B,SAAS,CAAC,CAAC,CAAC,cAAc,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAA;YAClH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,gBAAgB,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;YACjF,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,iBAAiB,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;YACrF,MAAM,IAAI,GAAQ,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;YACvC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;gBACtC,IAAI,CAAC,KAAK,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;oBACzE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAA;gBACvD,CAAC;YACH,CAAC;YACD,SAAS,GAAG,IAAI,CAAC,aAAa,IAAI,EAAE,CAAA;QACtC,CAAC,QAAQ,SAAS,EAAC;QACnB,OAAO,GAAG,CAAC,IAAI,EAAE,CAAA;IACnB,CAAC;IAED,IAAY,cAAc;QACxB,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;IAC7D,CAAC;CACF;AA5KD,wCA4KC;AAED,8FAA8F;AAC9F,MAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAqB,MAAc,EAAE,IAAY;QAC/C,KAAK,CAAC,0BAA0B,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAA;QAD7C,WAAM,GAAN,MAAM,CAAQ;IAEnC,CAAC;CACF;AAJD,8CAIC;AAED,SAAS,WAAW,CAAC,MAAW,EAAE,OAAsB,EAAE,SAAiB;IACzE,MAAM,KAAK,GAAG,MAAM,EAAE,QAAQ,EAAE,aAAa,CAAA;IAC7C,OAAO,CAAC,OAAO,EAAE,CAAC;QAChB,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,IAAI,SAAS;QAClD,WAAW,EAAE,KAAK,EAAE,gBAAgB;QACpC,YAAY,EAAE,KAAK,EAAE,oBAAoB;KAC1C,CAAC,CAAA;AACJ,CAAC;AAED,qEAAqE;AAErE,SAAS,oBAAoB,CAAC,OAAsB;IAClD,OAAO;QACL,eAAe,EAAE,OAAO,CAAC,SAAS,IAAI,kBAAkB;QACxD,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;QAC9E,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;KACvE,CAAA;AACH,CAAC;AAED,SAAS,aAAa,CACpB,QAAqB,EACrB,cAAuB;IAEvB,MAAM,cAAc,GAAG,QAAQ;SAC5B,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;SAChC,GAAG,CAAC,kBAAkB,CAAC;SACvB,MAAM,CAAC,OAAO,CAAC,CAAA;IAClB,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA;IAC9D,MAAM,YAAY,GAAG,CAAC,cAAc,EAAE,GAAG,cAAc,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACrF,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAA;AACtE,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAc;IACxC,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,OAAO,CAAA;IACvD,OAAO,GAAG,CAAC,OAAO;SACf,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;SAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAE,CAAoC,CAAC,IAAI,CAAC;SACpD,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC;AAED,SAAS,eAAe,CAAC,GAAc;IACrC,+CAA+C;IAC/C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAA;IAC1D,MAAM,KAAK,GACT,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;QAC7B,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;QACzB,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;IACnC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;AACxB,CAAC;AAED,SAAS,YAAY,CAAC,IAAmB;IACvC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACzB,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAA;IAC5B,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAC7B,uEAAuE;QACvE,qEAAqE;QACrE,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,gBAAgB,CAAA;QAC/C,OAAO;YACL,YAAY,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE;YACvD,GAAG,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE,CAAC;SAC5E,CAAA;IACH,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAChC,uEAAuE;QACvE,kDAAkD;QAClD,IAAI,MAAM,GAAQ,IAAI,CAAC,OAAO,CAAA;QAC9B,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACnC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,CAAA;QACnC,CAAC;QACD,OAAO;YACL,gBAAgB,EAAE;gBAChB,sDAAsD;gBACtD,qDAAqD;gBACrD,2DAA2D;gBAC3D,IAAI,EAAE,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAC;gBAC7C,QAAQ,EAAE,MAAM;aACjB;SACF,CAAA;IACH,CAAC;IACD,QAAQ;IACR,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC/B,8DAA8D;QAC9D,MAAM,IAAI,KAAK,CACb,wFAAwF,CACzF,CAAA;IACH,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;QAC5C,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACrC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACpD,OAAO;QACL,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE;KACtD,CAAA;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,uBAAuB,CAAC,EAAU;IACzC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC3B,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IACvB,CAAC;IACD,OAAO,EAAE,CAAA;AACX,CAAC;AAED;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IACnC,sBAAsB;IACtB,SAAS;IACT,OAAO;IACP,MAAM;IACN,KAAK;IACL,aAAa;IACb,mBAAmB;IACnB,cAAc;CACf,CAAC,CAAA;AAEF,SAAS,uBAAuB,CAAC,MAAW;IAC1C,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAA;IAC1D,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;IACrE,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAA;IAC7C,MAAM,GAAG,GAAQ,EAAE,CAAA;IACnB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5C,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,SAAQ;QACzC,GAAG,CAAC,CAAC,CAAC,GAAG,uBAAuB,CAAC,CAAC,CAAC,CAAA;IACrC,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,qBAAqB,CAAC,OAA0B;IACvD,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAA;IAC3C,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;QACtB,OAAO,EAAE,qBAAqB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAA;IACpD,CAAC;IACD,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QAC1B,OAAO;YACL,qBAAqB,EAAE;gBACrB,IAAI,EAAE,KAAK;gBACX,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE,oBAAoB,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;aACxE;SACF,CAAA;IACH,CAAC;IACD,OAAO,EAAE,qBAAqB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAA;AACpD,CAAC;AAED,SAAS,yBAAyB,CAChC,GAAQ,EACR,YAAqB;IAErB,gEAAgE;IAChE,0BAA0B;IAC1B,IAAI,YAAY;QAAE,OAAO,UAAU,CAAA;IACnC,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,MAAM;YACT,OAAO,UAAU,CAAA;QACnB,KAAK,YAAY;YACf,OAAO,YAAY,CAAA;QACrB,KAAK,QAAQ,CAAC;QACd,KAAK,YAAY,CAAC;QAClB,KAAK,OAAO;YACV,OAAO,eAAe,CAAA;QACxB;YACE,OAAO,GAAG,IAAI,UAAU,CAAA;IAC5B,CAAC;AACH,CAAC;AAED,+BAA+B;AAC/B,IAAA,+BAAkB,EAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC,CAAA","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"]}
|
|
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, { cutShort: result.response.candidates?.[0]?.finishReason === 'MAX_TOKENS' })\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"]}
|
|
@@ -59,7 +59,7 @@ class OpenAIAIClient {
|
|
|
59
59
|
});
|
|
60
60
|
reportUsage(response, options, options.model ?? this.defaultModel);
|
|
61
61
|
const text = response.choices[0]?.message?.content ?? '{}';
|
|
62
|
-
return (0, parse_json_js_1.parseJSONResponse)(text);
|
|
62
|
+
return (0, parse_json_js_1.parseJSONResponse)(text, { cutShort: response.choices[0]?.finish_reason === 'length' });
|
|
63
63
|
}
|
|
64
64
|
async vision(image, prompt, options = {}) {
|
|
65
65
|
const imagePart = typeof image === 'object' && 'url' in image
|
|
@@ -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 * 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"]}
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../../server/service/providers/openai.ts"],"names":[],"mappings":";;;AA6KA,8CAGC;AAsCD,sCAqCC;;AA3PD;;;;;;;GAOG;AACH,4DAA2B;AAc3B,8CAAkD;AAClD,0DAA0D;AAE1D,MAAM,aAAa,GAAG,QAAQ,CAAA;AAC9B,MAAM,kBAAkB,GAAG,IAAI,CAAA;AAE/B,MAAa,cAAc;IAKzB,YAAY,MAAsB;QAChC,IAAI,CAAC,GAAG,GAAG,IAAI,gBAAM,CAAC;YACpB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC;YACjE,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;SACnD,CAAC,CAAA;QACF,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,KAAK,IAAI,aAAa,CAAA;QACjD,IAAI,CAAC,EAAE,GAAG,UAAU,IAAI,CAAC,YAAY,EAAE,CAAA;IACzC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,QAAqB,EAAE,UAAyB,EAAE;QAC3D,MAAM,WAAW,GAAG,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;QACpE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YACtD,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY;YACzC,gGAAgG;YAChG,qBAAqB,EAAE,OAAO,CAAC,SAAS,IAAI,kBAAkB;YAC9D,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;YAC9E,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;YAC7D,QAAQ,EAAE,WAAkB;SAC7B,CAAC,CAAA;QACF,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,CAAA;QAClE,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,CAAA;IACpD,CAAC;IAED,KAAK,CAAC,YAAY,CAChB,MAA4B,EAC5B,UAAyB,EAAE;QAE3B,MAAM,QAAQ,GACZ,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAA;QAE3E,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM;YAC/B,CAAC,CAAC,mDAAmD,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;YACrF,CAAC,CAAC,EAAE,CAAA;QACN,MAAM,cAAc,GAClB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3D,iEAAiE;YACjE,UAAU,CAAA;QAEZ,MAAM,WAAW,GAAG,gBAAgB,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAA;QAC9D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YACtD,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY;YACzC,gGAAgG;YAChG,qBAAqB,EAAE,OAAO,CAAC,SAAS,IAAI,kBAAkB;YAC9D,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;YAC9E,eAAe,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE;YACxC,QAAQ,EAAE,WAAkB;SAC7B,CAAC,CAAA;QACF,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,CAAA;QAClE,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,IAAI,CAAA;QAC1D,OAAO,IAAA,iCAAiB,EAAI,IAAI,EAAE,EAAE,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,aAAa,KAAK,QAAQ,EAAE,CAAC,CAAA;IAClG,CAAC;IAED,KAAK,CAAC,MAAM,CACV,KAA4C,EAC5C,MAAc,EACd,UAA2B,EAAE;QAE7B,MAAM,SAAS,GACb,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,IAAI,KAAK;YACzC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,EAAE;YAC5D,CAAC,CAAC;gBACE,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,KAA4B,EAAE,SAAS,EAAE,WAAW,EAAE;aACvF,CAAA;QAEP,OAAO,IAAI,CAAC,IAAI,CACd,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,EACxE,OAAO,CACR,CAAA;IACH,CAAC;IAED,KAAK,CAAC,aAAa,CACjB,QAAqB,EACrB,KAAe,EACf,UAA6B,EAAE;QAE/B,MAAM,WAAW,GAAG,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;QAEpE,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC/B,IAAI,EAAE,UAAmB;YACzB,QAAQ,EAAE;gBACR,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;aACzB;SACF,CAAC,CAAC,CAAA;QAEH,MAAM,UAAU,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAA;QAEjD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YACtD,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY;YACzC,gGAAgG;YAChG,qBAAqB,EAAE,OAAO,CAAC,SAAS,IAAI,kBAAkB;YAC9D,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;YAC9E,GAAG,CAAC,OAAO,CAAC,aAAa,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;YAC7D,QAAQ,EAAE,WAAkB;YAC5B,KAAK,EAAE,QAAQ;YACf,GAAG,CAAC,UAAU,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,UAAiB,EAAE,CAAC;YACnE,GAAG,CAAC,OAAO,CAAC,sBAAsB,KAAK,KAAK,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,CAAC;SAChF,CAAC,CAAA;QACF,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,CAAA;QAElE,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;QACpC,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,CAAA;QAC/B,MAAM,IAAI,GAAG,OAAO,EAAE,OAAO,IAAI,SAAS,CAAA;QAE1C,MAAM,SAAS,GAAiB,CAAC,OAAO,EAAE,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAO,EAAE,EAAE;YAC1E,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAA;YACtB,IAAI,IAAI,GAAwB,EAAE,CAAA;YAClC,IAAI,CAAC;gBACH,IAAI,GAAG,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;YACtD,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,GAAG,EAAE,CAAA;YACX,CAAC;YACD,OAAO;gBACL,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE;gBAClG,IAAI,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE;gBACpB,SAAS,EAAE,IAAI;aAChB,CAAA;QACH,CAAC,CAAC,CAAA;QAEF,OAAO;YACL,IAAI;YACJ,SAAS;YACT,UAAU,EAAE,yBAAyB,CAAC,MAAM,EAAE,aAAa,CAAC;SAC7D,CAAA;IACH,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,GAAG,GAAa,EAAE,CAAA;QACxB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;YAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACpE,OAAO,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,IAAI,EAAE,CAAA;IAC7C,CAAC;CACF;AA3ID,wCA2IC;AAED;;;GAGG;AACH,SAAgB,iBAAiB,CAAC,EAAU;IAC1C,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAA;IAClD,OAAO,CAAC,4EAA4E,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAC/F,CAAC;AAED,SAAS,WAAW,CAAC,QAAa,EAAE,OAAsB,EAAE,SAAiB;IAC3E,OAAO,CAAC,OAAO,EAAE,CAAC;QAChB,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,SAAS;QACnC,WAAW,EAAE,QAAQ,EAAE,KAAK,EAAE,aAAa;QAC3C,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB;KACjD,CAAC,CAAA;AACJ,CAAC;AAED,qEAAqE;AAErE,SAAS,gBAAgB,CAAC,QAAqB,EAAE,cAAuB;IACtE,MAAM,GAAG,GAAU,EAAE,CAAA;IAErB,MAAM,kBAAkB,GAAG,QAAQ;SAChC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,CAAC;SAChC,GAAG,CAAC,kBAAkB,CAAC;SACvB,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,MAAM,CAAC,CAAA;IACf,MAAM,cAAc,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACxF,IAAI,cAAc,EAAE,CAAC;QACnB,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAA;IACvD,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,IAAI,GAAG,CAAC,IAAI,KAAK,QAAQ;YAAE,SAAQ;QACnC,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;QAClC,GAAG,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAA;IACtB,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa,CAAC,GAAc;IAC1C,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;IACnD,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAA;IACzB,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAU,CAAA;IACtE,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,aAAa,CAAU,CAAA;IAC5E,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa,CAAC,CAAA;IAEvF,2DAA2D;IAC3D,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;YAC/C,IAAI,EAAE,MAAM;YACZ,YAAY,EAAE,CAAC,CAAC,SAAS;YACzB,OAAO,EAAE,CAAC,CAAC,OAAO;SACnB,CAAC,CAAC,CAAA;QACH,8HAA8H;QAC9H,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAA;QACzD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;IACxG,CAAC;IAED,kDAAkD;IAClD,MAAM,GAAG,GAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAA;IACnC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,GAAG,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;IAChD,CAAC;SAAM,CAAC;QACN,GAAG,CAAC,OAAO,GAAG,IAAI,CAAA;IACpB,CAAC;IACD,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,GAAG,CAAC,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACtC,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE;SACnE,CAAC,CAAC,CAAA;IACL,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,CAAA;AACd,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAmB;IAC3C,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACzB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAA;IAC1C,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAC5D,kCAAkC;QAClC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,CAAA;IACnC,CAAC;IACD,QAAQ;IACR,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC/B,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,CAAA;IACnE,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;QAC3C,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACrC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACpD,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,SAAS,EAAE,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC,MAAM,CAAC,SAAS,WAAW,GAAG,EAAE,EAAE;KAClE,CAAA;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAc;IACxC,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,OAAO,CAAA;IACvD,OAAO,GAAG,CAAC,OAAO;SACf,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;SAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,CAAE,CAAoC,CAAC,IAAI,CAAC;SACpD,IAAI,CAAC,IAAI,CAAC,CAAA;AACf,CAAC;AAED,SAAS,qBAAqB,CAAC,OAA0B;IACvD,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,IAAI,MAAM,CAAA;IAC3C,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,MAAM,CAAA;IACpC,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;QAC1B,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,SAAS,EAAE,EAAE,CAAA;QACpE,CAAC;QACD,OAAO,UAAU,CAAA;IACnB,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAS,yBAAyB,CAAC,GAAQ;IACzC,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,YAAY;YACf,OAAO,UAAU,CAAA;QACnB,KAAK,eAAe;YAClB,OAAO,UAAU,CAAA;QACnB,KAAK,MAAM;YACT,OAAO,UAAU,CAAA;QACnB,KAAK,QAAQ;YACX,OAAO,YAAY,CAAA;QACrB,KAAK,gBAAgB;YACnB,OAAO,eAAe,CAAA;QACxB;YACE,OAAO,GAAG,IAAI,UAAU,CAAA;IAC5B,CAAC;AACH,CAAC;AAED,+BAA+B;AAC/B,IAAA,+BAAkB,EAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC,CAAA","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, { cutShort: response.choices[0]?.finish_reason === 'length' })\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"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ToolCallContext } from './types';
|
|
2
|
+
export interface ToolCallRecord {
|
|
3
|
+
tool: string;
|
|
4
|
+
/** The category the tool was offered from. */
|
|
5
|
+
category: string;
|
|
6
|
+
args: unknown;
|
|
7
|
+
/** What the tool returned - absent when it threw. A refusal it returned (`{ rejected: true }`) is here. */
|
|
8
|
+
result?: unknown;
|
|
9
|
+
/** Why it threw. */
|
|
10
|
+
error?: string;
|
|
11
|
+
durationMs: number;
|
|
12
|
+
}
|
|
13
|
+
export type ToolCallRecorder = (record: ToolCallRecord, context: ToolCallContext) => void | Promise<void>;
|
|
14
|
+
export declare function registerToolCallRecorder(recorder: ToolCallRecorder): void;
|
|
15
|
+
export declare function clearToolCallRecorders(): void;
|
|
16
|
+
export declare function recordToolCall(record: ToolCallRecord, context: ToolCallContext): Promise<void>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerToolCallRecorder = registerToolCallRecorder;
|
|
4
|
+
exports.clearToolCallRecorders = clearToolCallRecorders;
|
|
5
|
+
exports.recordToolCall = recordToolCall;
|
|
6
|
+
const recorders = [];
|
|
7
|
+
function registerToolCallRecorder(recorder) {
|
|
8
|
+
if (!recorders.includes(recorder))
|
|
9
|
+
recorders.push(recorder);
|
|
10
|
+
}
|
|
11
|
+
function clearToolCallRecorders() {
|
|
12
|
+
recorders.length = 0;
|
|
13
|
+
}
|
|
14
|
+
async function recordToolCall(record, context) {
|
|
15
|
+
for (const recorder of recorders) {
|
|
16
|
+
try {
|
|
17
|
+
await recorder(record, context);
|
|
18
|
+
}
|
|
19
|
+
catch (e) {
|
|
20
|
+
console.warn(`[ai-client-base] a tool call recorder failed for ${record.tool}:`, e?.message ?? e);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=call-recorder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-recorder.js","sourceRoot":"","sources":["../../../server/service/tool-registry/call-recorder.ts"],"names":[],"mappings":";;AA8BA,4DAEC;AAED,wDAEC;AAED,wCAQC;AAlBD,MAAM,SAAS,GAAuB,EAAE,CAAA;AAExC,SAAgB,wBAAwB,CAAC,QAA0B;IACjE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;AAC7D,CAAC;AAED,SAAgB,sBAAsB;IACpC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAA;AACtB,CAAC;AAEM,KAAK,UAAU,cAAc,CAAC,MAAsB,EAAE,OAAwB;IACnF,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACjC,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,IAAI,CAAC,oDAAoD,MAAM,CAAC,IAAI,GAAG,EAAG,CAAW,EAAE,OAAO,IAAI,CAAC,CAAC,CAAA;QAC9G,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["/*\n * Every tool call of a registry conversation, handed to whoever writes them down.\n *\n * An application that keeps an AI audit (operato-office's AI 감사 기록) needs each call - which tool, with what, what\n * came back or why it failed - whoever registered the tool: its own tools, a read target's list query, a data entry\n * target's proposal. Wrapping each tool in the application left out the tools other packages declare, so the audit\n * missed the reads that mattered most (2026-10-16). The registry conversation runs every tool at one place and calls\n * the recorders there.\n *\n * A recorder that fails is said in the log and the call goes on - an audit that cannot be written never stops an\n * answer. A recorder decides for itself which conversations it writes down (`context.mode.kind`).\n */\nimport type { ToolCallContext } from './types'\n\nexport interface ToolCallRecord {\n tool: string\n /** The category the tool was offered from. */\n category: string\n args: unknown\n /** What the tool returned - absent when it threw. A refusal it returned (`{ rejected: true }`) is here. */\n result?: unknown\n /** Why it threw. */\n error?: string\n durationMs: number\n}\n\nexport type ToolCallRecorder = (record: ToolCallRecord, context: ToolCallContext) => void | Promise<void>\n\nconst recorders: ToolCallRecorder[] = []\n\nexport function registerToolCallRecorder(recorder: ToolCallRecorder): void {\n if (!recorders.includes(recorder)) recorders.push(recorder)\n}\n\nexport function clearToolCallRecorders(): void {\n recorders.length = 0\n}\n\nexport async function recordToolCall(record: ToolCallRecord, context: ToolCallContext): Promise<void> {\n for (const recorder of recorders) {\n try {\n await recorder(record, context)\n } catch (e) {\n console.warn(`[ai-client-base] a tool call recorder failed for ${record.tool}:`, (e as Error)?.message ?? e)\n }\n }\n}\n"]}
|
|
@@ -5,5 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { registerToolCategory, getToolCategories, getAllToolSpecs, getToolSpecsFor, getCategoryGuidance, findToolSpec, getToolKind, hasTool, getCategorySpecs, clearToolRegistry, getToolRegistryWarnings } from './registry';
|
|
7
7
|
export { defaultValueTranslate } from './translate';
|
|
8
|
+
export { registerToolCallRecorder, clearToolCallRecorders, recordToolCall } from './call-recorder';
|
|
9
|
+
export type { ToolCallRecord, ToolCallRecorder } from './call-recorder';
|
|
8
10
|
export type { ToolCategoryFilter, ToolRegistryWarning } from './registry';
|
|
9
11
|
export type { ToolSpec, ToolSpecBase, ReadToolSpec, WriteToolSpec, ActionToolSpec, ExternalToolSpec, ToolCategory, ReadToolBuilder, WriteToolBuilder, ActionToolBuilder, ExternalToolBuilder, ReadToolContext, WriteToolContext, ExternalToolContext, ToolCallContext, ToolCallMode, ToolTranslate, ToolAttachment, ToolImageAttachment, ToolTextAttachment } from './types';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.defaultValueTranslate = exports.getToolRegistryWarnings = exports.clearToolRegistry = exports.getCategorySpecs = exports.hasTool = exports.getToolKind = exports.findToolSpec = exports.getCategoryGuidance = exports.getToolSpecsFor = exports.getAllToolSpecs = exports.getToolCategories = exports.registerToolCategory = void 0;
|
|
3
|
+
exports.recordToolCall = exports.clearToolCallRecorders = exports.registerToolCallRecorder = exports.defaultValueTranslate = exports.getToolRegistryWarnings = exports.clearToolRegistry = exports.getCategorySpecs = exports.hasTool = exports.getToolKind = exports.findToolSpec = exports.getCategoryGuidance = exports.getToolSpecsFor = exports.getAllToolSpecs = exports.getToolCategories = exports.registerToolCategory = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Tool Registry — board-ai 의 외부 도메인 LLM tool 노출 plugin point.
|
|
6
6
|
*
|
|
@@ -20,4 +20,8 @@ Object.defineProperty(exports, "clearToolRegistry", { enumerable: true, get: fun
|
|
|
20
20
|
Object.defineProperty(exports, "getToolRegistryWarnings", { enumerable: true, get: function () { return registry_1.getToolRegistryWarnings; } });
|
|
21
21
|
var translate_1 = require("./translate");
|
|
22
22
|
Object.defineProperty(exports, "defaultValueTranslate", { enumerable: true, get: function () { return translate_1.defaultValueTranslate; } });
|
|
23
|
+
var call_recorder_1 = require("./call-recorder");
|
|
24
|
+
Object.defineProperty(exports, "registerToolCallRecorder", { enumerable: true, get: function () { return call_recorder_1.registerToolCallRecorder; } });
|
|
25
|
+
Object.defineProperty(exports, "clearToolCallRecorders", { enumerable: true, get: function () { return call_recorder_1.clearToolCallRecorders; } });
|
|
26
|
+
Object.defineProperty(exports, "recordToolCall", { enumerable: true, get: function () { return call_recorder_1.recordToolCall; } });
|
|
23
27
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/tool-registry/index.ts"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,uCAYmB;AAXjB,gHAAA,oBAAoB,OAAA;AACpB,6GAAA,iBAAiB,OAAA;AACjB,2GAAA,eAAe,OAAA;AACf,2GAAA,eAAe,OAAA;AACf,+GAAA,mBAAmB,OAAA;AACnB,wGAAA,YAAY,OAAA;AACZ,uGAAA,WAAW,OAAA;AACX,mGAAA,OAAO,OAAA;AACP,4GAAA,gBAAgB,OAAA;AAChB,6GAAA,iBAAiB,OAAA;AACjB,mHAAA,uBAAuB,OAAA;AAGzB,yCAAmD;AAA1C,kHAAA,qBAAqB,OAAA","sourcesContent":["/**\n * Tool Registry — board-ai 의 외부 도메인 LLM tool 노출 plugin point.\n *\n * 자세한 설계는 ./types 와 ./registry 의 헤더 주석 참조.\n */\nexport {\n registerToolCategory,\n getToolCategories,\n getAllToolSpecs,\n getToolSpecsFor,\n getCategoryGuidance,\n findToolSpec,\n getToolKind,\n hasTool,\n getCategorySpecs,\n clearToolRegistry,\n getToolRegistryWarnings\n} from './registry'\n\nexport { defaultValueTranslate } from './translate'\n\n/* 대화별 노출 제한 타입은 registry 소유(정의가 거기 있다). */\nexport type { ToolCategoryFilter, ToolRegistryWarning } from './registry'\n\nexport type {\n ToolSpec,\n ToolSpecBase,\n ReadToolSpec,\n WriteToolSpec,\n ActionToolSpec,\n ExternalToolSpec,\n ToolCategory,\n ReadToolBuilder,\n WriteToolBuilder,\n ActionToolBuilder,\n ExternalToolBuilder,\n ReadToolContext,\n WriteToolContext,\n ExternalToolContext,\n ToolCallContext,\n ToolCallMode,\n ToolTranslate,\n ToolAttachment,\n ToolImageAttachment,\n ToolTextAttachment\n} from './types'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/service/tool-registry/index.ts"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,uCAYmB;AAXjB,gHAAA,oBAAoB,OAAA;AACpB,6GAAA,iBAAiB,OAAA;AACjB,2GAAA,eAAe,OAAA;AACf,2GAAA,eAAe,OAAA;AACf,+GAAA,mBAAmB,OAAA;AACnB,wGAAA,YAAY,OAAA;AACZ,uGAAA,WAAW,OAAA;AACX,mGAAA,OAAO,OAAA;AACP,4GAAA,gBAAgB,OAAA;AAChB,6GAAA,iBAAiB,OAAA;AACjB,mHAAA,uBAAuB,OAAA;AAGzB,yCAAmD;AAA1C,kHAAA,qBAAqB,OAAA;AAE9B,iDAAkG;AAAzF,yHAAA,wBAAwB,OAAA;AAAE,uHAAA,sBAAsB,OAAA;AAAE,+GAAA,cAAc,OAAA","sourcesContent":["/**\n * Tool Registry — board-ai 의 외부 도메인 LLM tool 노출 plugin point.\n *\n * 자세한 설계는 ./types 와 ./registry 의 헤더 주석 참조.\n */\nexport {\n registerToolCategory,\n getToolCategories,\n getAllToolSpecs,\n getToolSpecsFor,\n getCategoryGuidance,\n findToolSpec,\n getToolKind,\n hasTool,\n getCategorySpecs,\n clearToolRegistry,\n getToolRegistryWarnings\n} from './registry'\n\nexport { defaultValueTranslate } from './translate'\n\nexport { registerToolCallRecorder, clearToolCallRecorders, recordToolCall } from './call-recorder'\nexport type { ToolCallRecord, ToolCallRecorder } from './call-recorder'\n\n/* 대화별 노출 제한 타입은 registry 소유(정의가 거기 있다). */\nexport type { ToolCategoryFilter, ToolRegistryWarning } from './registry'\n\nexport type {\n ToolSpec,\n ToolSpecBase,\n ReadToolSpec,\n WriteToolSpec,\n ActionToolSpec,\n ExternalToolSpec,\n ToolCategory,\n ReadToolBuilder,\n WriteToolBuilder,\n ActionToolBuilder,\n ExternalToolBuilder,\n ReadToolContext,\n WriteToolContext,\n ExternalToolContext,\n ToolCallContext,\n ToolCallMode,\n ToolTranslate,\n ToolAttachment,\n ToolImageAttachment,\n ToolTextAttachment\n} from './types'\n"]}
|
|
@@ -12,7 +12,18 @@
|
|
|
12
12
|
* 2) 첫 balanced JSON object/array 만 잘라냄 (brace counting + 문자열 인지)
|
|
13
13
|
* 3) 그래도 실패하면 raw 출력 포함한 명확한 에러
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The model answered, and the answer is not JSON (figure audit 2026-10-16 N2). Told apart from a request that failed --
|
|
17
|
+
* a rejected key, a rate limit, a dropped connection -- because only this is worth asking again, with why it was not.
|
|
18
|
+
* `cutShort`: the answer stopped at the output limit before it ended, so asking again for the same is cut off again.
|
|
19
|
+
*/
|
|
20
|
+
export declare class AIAnswerNotJSON extends Error {
|
|
21
|
+
readonly cutShort: boolean;
|
|
22
|
+
constructor(message: string, cutShort?: boolean);
|
|
23
|
+
}
|
|
24
|
+
export declare function parseJSONResponse<T = unknown>(text: string, { cutShort }?: {
|
|
25
|
+
cutShort?: boolean;
|
|
26
|
+
}): T;
|
|
16
27
|
/**
|
|
17
28
|
* 첫 balanced { ... } 또는 [ ... ] 추출.
|
|
18
29
|
* 문자열 내의 brace 는 무시. escape 처리.
|
|
@@ -14,11 +14,25 @@
|
|
|
14
14
|
* 3) 그래도 실패하면 raw 출력 포함한 명확한 에러
|
|
15
15
|
*/
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.AIAnswerNotJSON = void 0;
|
|
17
18
|
exports.parseJSONResponse = parseJSONResponse;
|
|
18
19
|
exports.extractFirstBalanced = extractFirstBalanced;
|
|
19
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The model answered, and the answer is not JSON (figure audit 2026-10-16 N2). Told apart from a request that failed --
|
|
22
|
+
* a rejected key, a rate limit, a dropped connection -- because only this is worth asking again, with why it was not.
|
|
23
|
+
* `cutShort`: the answer stopped at the output limit before it ended, so asking again for the same is cut off again.
|
|
24
|
+
*/
|
|
25
|
+
class AIAnswerNotJSON extends Error {
|
|
26
|
+
constructor(message, cutShort = false) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.cutShort = cutShort;
|
|
29
|
+
this.name = 'AIAnswerNotJSON';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.AIAnswerNotJSON = AIAnswerNotJSON;
|
|
33
|
+
function parseJSONResponse(text, { cutShort = false } = {}) {
|
|
20
34
|
if (typeof text !== 'string') {
|
|
21
|
-
throw new
|
|
35
|
+
throw new AIAnswerNotJSON(`AI response is not a string (got ${typeof text})`, cutShort);
|
|
22
36
|
}
|
|
23
37
|
const stripped = stripCodeFences(text).trim();
|
|
24
38
|
// 1) 직접 시도
|
|
@@ -35,10 +49,10 @@ function parseJSONResponse(text) {
|
|
|
35
49
|
return JSON.parse(extracted);
|
|
36
50
|
}
|
|
37
51
|
catch (e) {
|
|
38
|
-
throw new
|
|
52
|
+
throw new AIAnswerNotJSON(`AI response is not valid JSON (after extraction): ${e.message}\n----- raw -----\n${text}`, cutShort);
|
|
39
53
|
}
|
|
40
54
|
}
|
|
41
|
-
throw new
|
|
55
|
+
throw new AIAnswerNotJSON(`AI response is not valid JSON: no balanced JSON found.\n----- raw -----\n${text}`, cutShort);
|
|
42
56
|
}
|
|
43
57
|
function stripCodeFences(text) {
|
|
44
58
|
return text
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-json.js","sourceRoot":"","sources":["../../../server/service/utils/parse-json.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;GAaG
|
|
1
|
+
{"version":3,"file":"parse-json.js","sourceRoot":"","sources":["../../../server/service/utils/parse-json.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Robust JSON parsing for LLM responses.\n *\n * LLM 응답은 다음 같은 변형이 흔하다:\n * - 마크다운 코드펜스로 감싸짐 (```json ... ```)\n * - 앞뒤 자연어 코멘트 (\"Sure, here is: { ... }\")\n * - 끝에 trailing 문자 (Gemini 의 알려진 케이스: 닫는 } 가 한 번 더)\n * - smart quote 등의 사소한 변형\n *\n * 이 helper 는:\n * 1) 코드펜스를 제거\n * 2) 첫 balanced JSON object/array 만 잘라냄 (brace counting + 문자열 인지)\n * 3) 그래도 실패하면 raw 출력 포함한 명확한 에러\n */\n\n/**\n * The model answered, and the answer is not JSON (figure audit 2026-10-16 N2). Told apart from a request that failed --\n * a rejected key, a rate limit, a dropped connection -- because only this is worth asking again, with why it was not.\n * `cutShort`: the answer stopped at the output limit before it ended, so asking again for the same is cut off again.\n */\nexport class AIAnswerNotJSON extends Error {\n constructor(\n message: string,\n readonly cutShort = false\n ) {\n super(message)\n this.name = 'AIAnswerNotJSON'\n }\n}\n\nexport function parseJSONResponse<T = unknown>(text: string, { cutShort = false }: { cutShort?: boolean } = {}): T {\n if (typeof text !== 'string') {\n throw new AIAnswerNotJSON(`AI response is not a string (got ${typeof text})`, cutShort)\n }\n\n const stripped = stripCodeFences(text).trim()\n\n // 1) 직접 시도\n try {\n return JSON.parse(stripped) as T\n } catch {\n /* fallthrough */\n }\n\n // 2) 첫 balanced JSON 추출 시도\n const extracted = extractFirstBalanced(stripped)\n if (extracted) {\n try {\n return JSON.parse(extracted) as T\n } catch (e) {\n throw new AIAnswerNotJSON(\n `AI response is not valid JSON (after extraction): ${(e as Error).message}\\n----- raw -----\\n${text}`,\n cutShort\n )\n }\n }\n\n throw new AIAnswerNotJSON(`AI response is not valid JSON: no balanced JSON found.\\n----- raw -----\\n${text}`, cutShort)\n}\n\nfunction stripCodeFences(text: string): string {\n return text\n .replace(/^\\s*```(?:json|JSON)?\\s*\\n?/i, '')\n .replace(/\\n?\\s*```\\s*$/i, '')\n}\n\n/**\n * 첫 balanced { ... } 또는 [ ... ] 추출.\n * 문자열 내의 brace 는 무시. escape 처리.\n */\nexport function extractFirstBalanced(text: string): string | null {\n const startIdx = findFirstBraceOrBracket(text)\n if (startIdx < 0) return null\n\n const opener = text[startIdx]\n const closer = opener === '{' ? '}' : ']'\n let depth = 0\n let inString = false\n let escape = false\n\n for (let i = startIdx; i < text.length; i++) {\n const ch = text[i]\n if (escape) {\n escape = false\n continue\n }\n if (inString) {\n if (ch === '\\\\') {\n escape = true\n } else if (ch === '\"') {\n inString = false\n }\n continue\n }\n if (ch === '\"') {\n inString = true\n continue\n }\n if (ch === opener) depth++\n else if (ch === closer) {\n depth--\n if (depth === 0) {\n return text.substring(startIdx, i + 1)\n }\n }\n }\n return null\n}\n\nfunction findFirstBraceOrBracket(text: string): number {\n for (let i = 0; i < text.length; i++) {\n const ch = text[i]\n if (ch === '{' || ch === '[') return i\n }\n return -1\n}\n"]}
|