@illuma-ai/agents 1.0.81
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +485 -0
- package/dist/cjs/agents/AgentContext.cjs +734 -0
- package/dist/cjs/agents/AgentContext.cjs.map +1 -0
- package/dist/cjs/common/enum.cjs +190 -0
- package/dist/cjs/common/enum.cjs.map +1 -0
- package/dist/cjs/events.cjs +172 -0
- package/dist/cjs/events.cjs.map +1 -0
- package/dist/cjs/graphs/Graph.cjs +1615 -0
- package/dist/cjs/graphs/Graph.cjs.map +1 -0
- package/dist/cjs/graphs/MultiAgentGraph.cjs +890 -0
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -0
- package/dist/cjs/instrumentation.cjs +21 -0
- package/dist/cjs/instrumentation.cjs.map +1 -0
- package/dist/cjs/llm/anthropic/index.cjs +292 -0
- package/dist/cjs/llm/anthropic/index.cjs.map +1 -0
- package/dist/cjs/llm/anthropic/types.cjs +50 -0
- package/dist/cjs/llm/anthropic/types.cjs.map +1 -0
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs +630 -0
- package/dist/cjs/llm/anthropic/utils/message_inputs.cjs.map +1 -0
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs +218 -0
- package/dist/cjs/llm/anthropic/utils/message_outputs.cjs.map +1 -0
- package/dist/cjs/llm/anthropic/utils/tools.cjs +29 -0
- package/dist/cjs/llm/anthropic/utils/tools.cjs.map +1 -0
- package/dist/cjs/llm/bedrock/index.cjs +282 -0
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -0
- package/dist/cjs/llm/fake.cjs +97 -0
- package/dist/cjs/llm/fake.cjs.map +1 -0
- package/dist/cjs/llm/google/index.cjs +216 -0
- package/dist/cjs/llm/google/index.cjs.map +1 -0
- package/dist/cjs/llm/google/utils/common.cjs +647 -0
- package/dist/cjs/llm/google/utils/common.cjs.map +1 -0
- package/dist/cjs/llm/openai/index.cjs +1028 -0
- package/dist/cjs/llm/openai/index.cjs.map +1 -0
- package/dist/cjs/llm/openai/utils/index.cjs +765 -0
- package/dist/cjs/llm/openai/utils/index.cjs.map +1 -0
- package/dist/cjs/llm/openrouter/index.cjs +212 -0
- package/dist/cjs/llm/openrouter/index.cjs.map +1 -0
- package/dist/cjs/llm/providers.cjs +43 -0
- package/dist/cjs/llm/providers.cjs.map +1 -0
- package/dist/cjs/llm/text.cjs +69 -0
- package/dist/cjs/llm/text.cjs.map +1 -0
- package/dist/cjs/llm/vertexai/index.cjs +329 -0
- package/dist/cjs/llm/vertexai/index.cjs.map +1 -0
- package/dist/cjs/main.cjs +240 -0
- package/dist/cjs/main.cjs.map +1 -0
- package/dist/cjs/messages/cache.cjs +387 -0
- package/dist/cjs/messages/cache.cjs.map +1 -0
- package/dist/cjs/messages/content.cjs +53 -0
- package/dist/cjs/messages/content.cjs.map +1 -0
- package/dist/cjs/messages/core.cjs +367 -0
- package/dist/cjs/messages/core.cjs.map +1 -0
- package/dist/cjs/messages/format.cjs +761 -0
- package/dist/cjs/messages/format.cjs.map +1 -0
- package/dist/cjs/messages/ids.cjs +23 -0
- package/dist/cjs/messages/ids.cjs.map +1 -0
- package/dist/cjs/messages/prune.cjs +398 -0
- package/dist/cjs/messages/prune.cjs.map +1 -0
- package/dist/cjs/messages/tools.cjs +96 -0
- package/dist/cjs/messages/tools.cjs.map +1 -0
- package/dist/cjs/run.cjs +328 -0
- package/dist/cjs/run.cjs.map +1 -0
- package/dist/cjs/schemas/validate.cjs +324 -0
- package/dist/cjs/schemas/validate.cjs.map +1 -0
- package/dist/cjs/splitStream.cjs +210 -0
- package/dist/cjs/splitStream.cjs.map +1 -0
- package/dist/cjs/stream.cjs +620 -0
- package/dist/cjs/stream.cjs.map +1 -0
- package/dist/cjs/tools/BrowserTools.cjs +248 -0
- package/dist/cjs/tools/BrowserTools.cjs.map +1 -0
- package/dist/cjs/tools/Calculator.cjs +66 -0
- package/dist/cjs/tools/Calculator.cjs.map +1 -0
- package/dist/cjs/tools/CodeExecutor.cjs +234 -0
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -0
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +636 -0
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -0
- package/dist/cjs/tools/ToolNode.cjs +548 -0
- package/dist/cjs/tools/ToolNode.cjs.map +1 -0
- package/dist/cjs/tools/ToolSearch.cjs +909 -0
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -0
- package/dist/cjs/tools/handlers.cjs +255 -0
- package/dist/cjs/tools/handlers.cjs.map +1 -0
- package/dist/cjs/tools/schema.cjs +31 -0
- package/dist/cjs/tools/schema.cjs.map +1 -0
- package/dist/cjs/tools/search/anthropic.cjs +40 -0
- package/dist/cjs/tools/search/anthropic.cjs.map +1 -0
- package/dist/cjs/tools/search/content.cjs +140 -0
- package/dist/cjs/tools/search/content.cjs.map +1 -0
- package/dist/cjs/tools/search/firecrawl.cjs +179 -0
- package/dist/cjs/tools/search/firecrawl.cjs.map +1 -0
- package/dist/cjs/tools/search/format.cjs +203 -0
- package/dist/cjs/tools/search/format.cjs.map +1 -0
- package/dist/cjs/tools/search/highlights.cjs +245 -0
- package/dist/cjs/tools/search/highlights.cjs.map +1 -0
- package/dist/cjs/tools/search/rerankers.cjs +174 -0
- package/dist/cjs/tools/search/rerankers.cjs.map +1 -0
- package/dist/cjs/tools/search/schema.cjs +117 -0
- package/dist/cjs/tools/search/schema.cjs.map +1 -0
- package/dist/cjs/tools/search/search.cjs +566 -0
- package/dist/cjs/tools/search/search.cjs.map +1 -0
- package/dist/cjs/tools/search/serper-scraper.cjs +132 -0
- package/dist/cjs/tools/search/serper-scraper.cjs.map +1 -0
- package/dist/cjs/tools/search/tool.cjs +456 -0
- package/dist/cjs/tools/search/tool.cjs.map +1 -0
- package/dist/cjs/tools/search/utils.cjs +66 -0
- package/dist/cjs/tools/search/utils.cjs.map +1 -0
- package/dist/cjs/types/graph.cjs +29 -0
- package/dist/cjs/types/graph.cjs.map +1 -0
- package/dist/cjs/utils/contextAnalytics.cjs +66 -0
- package/dist/cjs/utils/contextAnalytics.cjs.map +1 -0
- package/dist/cjs/utils/events.cjs +31 -0
- package/dist/cjs/utils/events.cjs.map +1 -0
- package/dist/cjs/utils/graph.cjs +16 -0
- package/dist/cjs/utils/graph.cjs.map +1 -0
- package/dist/cjs/utils/handlers.cjs +70 -0
- package/dist/cjs/utils/handlers.cjs.map +1 -0
- package/dist/cjs/utils/llm.cjs +27 -0
- package/dist/cjs/utils/llm.cjs.map +1 -0
- package/dist/cjs/utils/misc.cjs +56 -0
- package/dist/cjs/utils/misc.cjs.map +1 -0
- package/dist/cjs/utils/run.cjs +73 -0
- package/dist/cjs/utils/run.cjs.map +1 -0
- package/dist/cjs/utils/schema.cjs +27 -0
- package/dist/cjs/utils/schema.cjs.map +1 -0
- package/dist/cjs/utils/title.cjs +125 -0
- package/dist/cjs/utils/title.cjs.map +1 -0
- package/dist/cjs/utils/tokens.cjs +125 -0
- package/dist/cjs/utils/tokens.cjs.map +1 -0
- package/dist/cjs/utils/toonFormat.cjs +388 -0
- package/dist/cjs/utils/toonFormat.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +732 -0
- package/dist/esm/agents/AgentContext.mjs.map +1 -0
- package/dist/esm/common/enum.mjs +190 -0
- package/dist/esm/common/enum.mjs.map +1 -0
- package/dist/esm/events.mjs +164 -0
- package/dist/esm/events.mjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +1612 -0
- package/dist/esm/graphs/Graph.mjs.map +1 -0
- package/dist/esm/graphs/MultiAgentGraph.mjs +888 -0
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -0
- package/dist/esm/instrumentation.mjs +19 -0
- package/dist/esm/instrumentation.mjs.map +1 -0
- package/dist/esm/llm/anthropic/index.mjs +290 -0
- package/dist/esm/llm/anthropic/index.mjs.map +1 -0
- package/dist/esm/llm/anthropic/types.mjs +48 -0
- package/dist/esm/llm/anthropic/types.mjs.map +1 -0
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs +627 -0
- package/dist/esm/llm/anthropic/utils/message_inputs.mjs.map +1 -0
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs +216 -0
- package/dist/esm/llm/anthropic/utils/message_outputs.mjs.map +1 -0
- package/dist/esm/llm/anthropic/utils/tools.mjs +27 -0
- package/dist/esm/llm/anthropic/utils/tools.mjs.map +1 -0
- package/dist/esm/llm/bedrock/index.mjs +280 -0
- package/dist/esm/llm/bedrock/index.mjs.map +1 -0
- package/dist/esm/llm/fake.mjs +94 -0
- package/dist/esm/llm/fake.mjs.map +1 -0
- package/dist/esm/llm/google/index.mjs +214 -0
- package/dist/esm/llm/google/index.mjs.map +1 -0
- package/dist/esm/llm/google/utils/common.mjs +638 -0
- package/dist/esm/llm/google/utils/common.mjs.map +1 -0
- package/dist/esm/llm/openai/index.mjs +1018 -0
- package/dist/esm/llm/openai/index.mjs.map +1 -0
- package/dist/esm/llm/openai/utils/index.mjs +759 -0
- package/dist/esm/llm/openai/utils/index.mjs.map +1 -0
- package/dist/esm/llm/openrouter/index.mjs +210 -0
- package/dist/esm/llm/openrouter/index.mjs.map +1 -0
- package/dist/esm/llm/providers.mjs +39 -0
- package/dist/esm/llm/providers.mjs.map +1 -0
- package/dist/esm/llm/text.mjs +67 -0
- package/dist/esm/llm/text.mjs.map +1 -0
- package/dist/esm/llm/vertexai/index.mjs +327 -0
- package/dist/esm/llm/vertexai/index.mjs.map +1 -0
- package/dist/esm/main.mjs +37 -0
- package/dist/esm/main.mjs.map +1 -0
- package/dist/esm/messages/cache.mjs +382 -0
- package/dist/esm/messages/cache.mjs.map +1 -0
- package/dist/esm/messages/content.mjs +51 -0
- package/dist/esm/messages/content.mjs.map +1 -0
- package/dist/esm/messages/core.mjs +359 -0
- package/dist/esm/messages/core.mjs.map +1 -0
- package/dist/esm/messages/format.mjs +752 -0
- package/dist/esm/messages/format.mjs.map +1 -0
- package/dist/esm/messages/ids.mjs +21 -0
- package/dist/esm/messages/ids.mjs.map +1 -0
- package/dist/esm/messages/prune.mjs +393 -0
- package/dist/esm/messages/prune.mjs.map +1 -0
- package/dist/esm/messages/tools.mjs +93 -0
- package/dist/esm/messages/tools.mjs.map +1 -0
- package/dist/esm/run.mjs +325 -0
- package/dist/esm/run.mjs.map +1 -0
- package/dist/esm/schemas/validate.mjs +317 -0
- package/dist/esm/schemas/validate.mjs.map +1 -0
- package/dist/esm/splitStream.mjs +207 -0
- package/dist/esm/splitStream.mjs.map +1 -0
- package/dist/esm/stream.mjs +616 -0
- package/dist/esm/stream.mjs.map +1 -0
- package/dist/esm/tools/BrowserTools.mjs +244 -0
- package/dist/esm/tools/BrowserTools.mjs.map +1 -0
- package/dist/esm/tools/Calculator.mjs +41 -0
- package/dist/esm/tools/Calculator.mjs.map +1 -0
- package/dist/esm/tools/CodeExecutor.mjs +226 -0
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -0
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +622 -0
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -0
- package/dist/esm/tools/ToolNode.mjs +545 -0
- package/dist/esm/tools/ToolNode.mjs.map +1 -0
- package/dist/esm/tools/ToolSearch.mjs +870 -0
- package/dist/esm/tools/ToolSearch.mjs.map +1 -0
- package/dist/esm/tools/handlers.mjs +250 -0
- package/dist/esm/tools/handlers.mjs.map +1 -0
- package/dist/esm/tools/schema.mjs +28 -0
- package/dist/esm/tools/schema.mjs.map +1 -0
- package/dist/esm/tools/search/anthropic.mjs +37 -0
- package/dist/esm/tools/search/anthropic.mjs.map +1 -0
- package/dist/esm/tools/search/content.mjs +119 -0
- package/dist/esm/tools/search/content.mjs.map +1 -0
- package/dist/esm/tools/search/firecrawl.mjs +176 -0
- package/dist/esm/tools/search/firecrawl.mjs.map +1 -0
- package/dist/esm/tools/search/format.mjs +201 -0
- package/dist/esm/tools/search/format.mjs.map +1 -0
- package/dist/esm/tools/search/highlights.mjs +243 -0
- package/dist/esm/tools/search/highlights.mjs.map +1 -0
- package/dist/esm/tools/search/rerankers.mjs +168 -0
- package/dist/esm/tools/search/rerankers.mjs.map +1 -0
- package/dist/esm/tools/search/schema.mjs +104 -0
- package/dist/esm/tools/search/schema.mjs.map +1 -0
- package/dist/esm/tools/search/search.mjs +563 -0
- package/dist/esm/tools/search/search.mjs.map +1 -0
- package/dist/esm/tools/search/serper-scraper.mjs +129 -0
- package/dist/esm/tools/search/serper-scraper.mjs.map +1 -0
- package/dist/esm/tools/search/tool.mjs +454 -0
- package/dist/esm/tools/search/tool.mjs.map +1 -0
- package/dist/esm/tools/search/utils.mjs +61 -0
- package/dist/esm/tools/search/utils.mjs.map +1 -0
- package/dist/esm/types/graph.mjs +26 -0
- package/dist/esm/types/graph.mjs.map +1 -0
- package/dist/esm/utils/contextAnalytics.mjs +64 -0
- package/dist/esm/utils/contextAnalytics.mjs.map +1 -0
- package/dist/esm/utils/events.mjs +29 -0
- package/dist/esm/utils/events.mjs.map +1 -0
- package/dist/esm/utils/graph.mjs +13 -0
- package/dist/esm/utils/graph.mjs.map +1 -0
- package/dist/esm/utils/handlers.mjs +68 -0
- package/dist/esm/utils/handlers.mjs.map +1 -0
- package/dist/esm/utils/llm.mjs +24 -0
- package/dist/esm/utils/llm.mjs.map +1 -0
- package/dist/esm/utils/misc.mjs +53 -0
- package/dist/esm/utils/misc.mjs.map +1 -0
- package/dist/esm/utils/run.mjs +70 -0
- package/dist/esm/utils/run.mjs.map +1 -0
- package/dist/esm/utils/schema.mjs +24 -0
- package/dist/esm/utils/schema.mjs.map +1 -0
- package/dist/esm/utils/title.mjs +122 -0
- package/dist/esm/utils/title.mjs.map +1 -0
- package/dist/esm/utils/tokens.mjs +121 -0
- package/dist/esm/utils/tokens.mjs.map +1 -0
- package/dist/esm/utils/toonFormat.mjs +381 -0
- package/dist/esm/utils/toonFormat.mjs.map +1 -0
- package/dist/types/agents/AgentContext.d.ts +293 -0
- package/dist/types/common/enum.d.ts +155 -0
- package/dist/types/common/index.d.ts +1 -0
- package/dist/types/events.d.ts +31 -0
- package/dist/types/graphs/Graph.d.ts +216 -0
- package/dist/types/graphs/MultiAgentGraph.d.ts +104 -0
- package/dist/types/graphs/index.d.ts +2 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/instrumentation.d.ts +1 -0
- package/dist/types/llm/anthropic/index.d.ts +39 -0
- package/dist/types/llm/anthropic/types.d.ts +37 -0
- package/dist/types/llm/anthropic/utils/message_inputs.d.ts +14 -0
- package/dist/types/llm/anthropic/utils/message_outputs.d.ts +14 -0
- package/dist/types/llm/anthropic/utils/output_parsers.d.ts +22 -0
- package/dist/types/llm/anthropic/utils/tools.d.ts +3 -0
- package/dist/types/llm/bedrock/index.d.ts +141 -0
- package/dist/types/llm/bedrock/types.d.ts +27 -0
- package/dist/types/llm/bedrock/utils/index.d.ts +5 -0
- package/dist/types/llm/bedrock/utils/message_inputs.d.ts +31 -0
- package/dist/types/llm/bedrock/utils/message_outputs.d.ts +33 -0
- package/dist/types/llm/fake.d.ts +31 -0
- package/dist/types/llm/google/index.d.ts +24 -0
- package/dist/types/llm/google/types.d.ts +42 -0
- package/dist/types/llm/google/utils/common.d.ts +34 -0
- package/dist/types/llm/google/utils/tools.d.ts +10 -0
- package/dist/types/llm/google/utils/zod_to_genai_parameters.d.ts +14 -0
- package/dist/types/llm/openai/index.d.ts +127 -0
- package/dist/types/llm/openai/types.d.ts +10 -0
- package/dist/types/llm/openai/utils/index.d.ts +29 -0
- package/dist/types/llm/openrouter/index.d.ts +15 -0
- package/dist/types/llm/providers.d.ts +5 -0
- package/dist/types/llm/text.d.ts +21 -0
- package/dist/types/llm/vertexai/index.d.ts +293 -0
- package/dist/types/messages/cache.d.ts +54 -0
- package/dist/types/messages/content.d.ts +7 -0
- package/dist/types/messages/core.d.ts +14 -0
- package/dist/types/messages/format.d.ts +137 -0
- package/dist/types/messages/ids.d.ts +3 -0
- package/dist/types/messages/index.d.ts +7 -0
- package/dist/types/messages/prune.d.ts +52 -0
- package/dist/types/messages/reducer.d.ts +9 -0
- package/dist/types/messages/tools.d.ts +17 -0
- package/dist/types/mockStream.d.ts +32 -0
- package/dist/types/prompts/collab.d.ts +1 -0
- package/dist/types/prompts/index.d.ts +2 -0
- package/dist/types/prompts/taskmanager.d.ts +41 -0
- package/dist/types/run.d.ts +41 -0
- package/dist/types/schemas/index.d.ts +1 -0
- package/dist/types/schemas/validate.d.ts +59 -0
- package/dist/types/splitStream.d.ts +37 -0
- package/dist/types/stream.d.ts +15 -0
- package/dist/types/test/mockTools.d.ts +28 -0
- package/dist/types/tools/BrowserTools.d.ts +87 -0
- package/dist/types/tools/Calculator.d.ts +34 -0
- package/dist/types/tools/CodeExecutor.d.ts +57 -0
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +138 -0
- package/dist/types/tools/ToolNode.d.ts +51 -0
- package/dist/types/tools/ToolSearch.d.ts +219 -0
- package/dist/types/tools/handlers.d.ts +22 -0
- package/dist/types/tools/schema.d.ts +12 -0
- package/dist/types/tools/search/anthropic.d.ts +16 -0
- package/dist/types/tools/search/content.d.ts +4 -0
- package/dist/types/tools/search/firecrawl.d.ts +54 -0
- package/dist/types/tools/search/format.d.ts +5 -0
- package/dist/types/tools/search/highlights.d.ts +13 -0
- package/dist/types/tools/search/index.d.ts +3 -0
- package/dist/types/tools/search/rerankers.d.ts +38 -0
- package/dist/types/tools/search/schema.d.ts +103 -0
- package/dist/types/tools/search/search.d.ts +8 -0
- package/dist/types/tools/search/serper-scraper.d.ts +59 -0
- package/dist/types/tools/search/test.d.ts +1 -0
- package/dist/types/tools/search/tool.d.ts +3 -0
- package/dist/types/tools/search/types.d.ts +575 -0
- package/dist/types/tools/search/utils.d.ts +10 -0
- package/dist/types/types/graph.d.ts +399 -0
- package/dist/types/types/index.d.ts +5 -0
- package/dist/types/types/llm.d.ts +105 -0
- package/dist/types/types/messages.d.ts +4 -0
- package/dist/types/types/run.d.ts +112 -0
- package/dist/types/types/stream.d.ts +308 -0
- package/dist/types/types/tools.d.ts +296 -0
- package/dist/types/utils/contextAnalytics.d.ts +37 -0
- package/dist/types/utils/events.d.ts +6 -0
- package/dist/types/utils/graph.d.ts +2 -0
- package/dist/types/utils/handlers.d.ts +34 -0
- package/dist/types/utils/index.d.ts +9 -0
- package/dist/types/utils/llm.d.ts +3 -0
- package/dist/types/utils/llmConfig.d.ts +3 -0
- package/dist/types/utils/logging.d.ts +1 -0
- package/dist/types/utils/misc.d.ts +7 -0
- package/dist/types/utils/run.d.ts +27 -0
- package/dist/types/utils/schema.d.ts +8 -0
- package/dist/types/utils/title.d.ts +4 -0
- package/dist/types/utils/tokens.d.ts +28 -0
- package/dist/types/utils/toonFormat.d.ts +111 -0
- package/package.json +190 -0
- package/src/agents/AgentContext.test.ts +458 -0
- package/src/agents/AgentContext.ts +972 -0
- package/src/agents/__tests__/AgentContext.test.ts +805 -0
- package/src/agents/__tests__/resolveStructuredOutputMode.test.ts +137 -0
- package/src/common/enum.ts +203 -0
- package/src/common/index.ts +2 -0
- package/src/events.ts +223 -0
- package/src/graphs/Graph.ts +2228 -0
- package/src/graphs/MultiAgentGraph.ts +1063 -0
- package/src/graphs/__tests__/structured-output.integration.test.ts +809 -0
- package/src/graphs/__tests__/structured-output.test.ts +183 -0
- package/src/graphs/index.ts +2 -0
- package/src/index.ts +34 -0
- package/src/instrumentation.ts +22 -0
- package/src/llm/anthropic/Jacob_Lee_Resume_2023.pdf +0 -0
- package/src/llm/anthropic/index.ts +413 -0
- package/src/llm/anthropic/llm.spec.ts +1442 -0
- package/src/llm/anthropic/types.ts +140 -0
- package/src/llm/anthropic/utils/message_inputs.ts +757 -0
- package/src/llm/anthropic/utils/message_outputs.ts +289 -0
- package/src/llm/anthropic/utils/output_parsers.ts +133 -0
- package/src/llm/anthropic/utils/tools.ts +29 -0
- package/src/llm/bedrock/__tests__/bedrock-caching.test.ts +495 -0
- package/src/llm/bedrock/index.ts +411 -0
- package/src/llm/bedrock/llm.spec.ts +616 -0
- package/src/llm/bedrock/types.ts +51 -0
- package/src/llm/bedrock/utils/index.ts +18 -0
- package/src/llm/bedrock/utils/message_inputs.ts +563 -0
- package/src/llm/bedrock/utils/message_outputs.ts +310 -0
- package/src/llm/fake.ts +133 -0
- package/src/llm/google/data/gettysburg10.wav +0 -0
- package/src/llm/google/data/hotdog.jpg +0 -0
- package/src/llm/google/index.ts +337 -0
- package/src/llm/google/llm.spec.ts +934 -0
- package/src/llm/google/types.ts +56 -0
- package/src/llm/google/utils/common.ts +873 -0
- package/src/llm/google/utils/tools.ts +160 -0
- package/src/llm/google/utils/zod_to_genai_parameters.ts +86 -0
- package/src/llm/openai/index.ts +1366 -0
- package/src/llm/openai/types.ts +24 -0
- package/src/llm/openai/utils/index.ts +1035 -0
- package/src/llm/openai/utils/isReasoningModel.test.ts +90 -0
- package/src/llm/openrouter/index.ts +291 -0
- package/src/llm/providers.ts +52 -0
- package/src/llm/text.ts +94 -0
- package/src/llm/vertexai/index.ts +359 -0
- package/src/messages/__tests__/tools.test.ts +473 -0
- package/src/messages/cache.test.ts +1261 -0
- package/src/messages/cache.ts +518 -0
- package/src/messages/content.test.ts +362 -0
- package/src/messages/content.ts +63 -0
- package/src/messages/core.ts +473 -0
- package/src/messages/ensureThinkingBlock.test.ts +468 -0
- package/src/messages/format.ts +1029 -0
- package/src/messages/formatAgentMessages.test.ts +1513 -0
- package/src/messages/formatAgentMessages.tools.test.ts +419 -0
- package/src/messages/formatMessage.test.ts +693 -0
- package/src/messages/ids.ts +26 -0
- package/src/messages/index.ts +7 -0
- package/src/messages/labelContentByAgent.test.ts +887 -0
- package/src/messages/prune.ts +568 -0
- package/src/messages/reducer.ts +80 -0
- package/src/messages/shiftIndexTokenCountMap.test.ts +81 -0
- package/src/messages/tools.ts +108 -0
- package/src/mockStream.ts +99 -0
- package/src/prompts/collab.ts +6 -0
- package/src/prompts/index.ts +2 -0
- package/src/prompts/taskmanager.ts +61 -0
- package/src/run.ts +467 -0
- package/src/schemas/index.ts +2 -0
- package/src/schemas/schema-preparation.test.ts +500 -0
- package/src/schemas/validate.test.ts +358 -0
- package/src/schemas/validate.ts +454 -0
- package/src/scripts/abort.ts +157 -0
- package/src/scripts/ant_web_search.ts +158 -0
- package/src/scripts/ant_web_search_edge_case.ts +162 -0
- package/src/scripts/ant_web_search_error_edge_case.ts +148 -0
- package/src/scripts/args.ts +48 -0
- package/src/scripts/caching.ts +132 -0
- package/src/scripts/cli.ts +172 -0
- package/src/scripts/cli2.ts +133 -0
- package/src/scripts/cli3.ts +184 -0
- package/src/scripts/cli4.ts +191 -0
- package/src/scripts/cli5.ts +191 -0
- package/src/scripts/code_exec.ts +213 -0
- package/src/scripts/code_exec_files.ts +236 -0
- package/src/scripts/code_exec_multi_session.ts +241 -0
- package/src/scripts/code_exec_ptc.ts +334 -0
- package/src/scripts/code_exec_session.ts +282 -0
- package/src/scripts/code_exec_simple.ts +147 -0
- package/src/scripts/content.ts +138 -0
- package/src/scripts/empty_input.ts +137 -0
- package/src/scripts/handoff-test.ts +135 -0
- package/src/scripts/image.ts +178 -0
- package/src/scripts/memory.ts +97 -0
- package/src/scripts/multi-agent-chain.ts +331 -0
- package/src/scripts/multi-agent-conditional.ts +221 -0
- package/src/scripts/multi-agent-document-review-chain.ts +197 -0
- package/src/scripts/multi-agent-hybrid-flow.ts +310 -0
- package/src/scripts/multi-agent-parallel-start.ts +265 -0
- package/src/scripts/multi-agent-parallel.ts +394 -0
- package/src/scripts/multi-agent-sequence.ts +217 -0
- package/src/scripts/multi-agent-supervisor.ts +365 -0
- package/src/scripts/multi-agent-test.ts +186 -0
- package/src/scripts/parallel-asymmetric-tools-test.ts +274 -0
- package/src/scripts/parallel-full-metadata-test.ts +240 -0
- package/src/scripts/parallel-tools-test.ts +340 -0
- package/src/scripts/programmatic_exec.ts +396 -0
- package/src/scripts/programmatic_exec_agent.ts +231 -0
- package/src/scripts/search.ts +146 -0
- package/src/scripts/sequential-full-metadata-test.ts +197 -0
- package/src/scripts/simple.ts +225 -0
- package/src/scripts/single-agent-metadata-test.ts +198 -0
- package/src/scripts/stream.ts +140 -0
- package/src/scripts/test-custom-prompt-key.ts +145 -0
- package/src/scripts/test-handoff-input.ts +170 -0
- package/src/scripts/test-handoff-preamble.ts +277 -0
- package/src/scripts/test-multi-agent-list-handoff.ts +417 -0
- package/src/scripts/test-parallel-agent-labeling.ts +325 -0
- package/src/scripts/test-parallel-handoffs.ts +291 -0
- package/src/scripts/test-thinking-handoff-bedrock.ts +153 -0
- package/src/scripts/test-thinking-handoff.ts +155 -0
- package/src/scripts/test-tools-before-handoff.ts +226 -0
- package/src/scripts/test_code_api.ts +361 -0
- package/src/scripts/thinking-bedrock.ts +159 -0
- package/src/scripts/thinking.ts +171 -0
- package/src/scripts/tool_search.ts +162 -0
- package/src/scripts/tools.ts +177 -0
- package/src/specs/agent-handoffs.test.ts +888 -0
- package/src/specs/anthropic.simple.test.ts +387 -0
- package/src/specs/azure.simple.test.ts +364 -0
- package/src/specs/cache.simple.test.ts +396 -0
- package/src/specs/deepseek.simple.test.ts +283 -0
- package/src/specs/emergency-prune.test.ts +407 -0
- package/src/specs/moonshot.simple.test.ts +358 -0
- package/src/specs/openai.simple.test.ts +311 -0
- package/src/specs/openrouter.simple.test.ts +107 -0
- package/src/specs/prune.test.ts +901 -0
- package/src/specs/reasoning.test.ts +201 -0
- package/src/specs/spec.utils.ts +3 -0
- package/src/specs/thinking-handoff.test.ts +620 -0
- package/src/specs/thinking-prune.test.ts +703 -0
- package/src/specs/token-distribution-edge-case.test.ts +316 -0
- package/src/specs/token-memoization.test.ts +32 -0
- package/src/specs/tool-error.test.ts +198 -0
- package/src/splitStream.test.ts +691 -0
- package/src/splitStream.ts +234 -0
- package/src/stream.test.ts +94 -0
- package/src/stream.ts +801 -0
- package/src/test/mockTools.ts +386 -0
- package/src/tools/BrowserTools.ts +393 -0
- package/src/tools/Calculator.test.ts +278 -0
- package/src/tools/Calculator.ts +46 -0
- package/src/tools/CodeExecutor.ts +270 -0
- package/src/tools/ProgrammaticToolCalling.ts +785 -0
- package/src/tools/ToolNode.ts +674 -0
- package/src/tools/ToolSearch.ts +1095 -0
- package/src/tools/__tests__/BrowserTools.test.ts +265 -0
- package/src/tools/__tests__/ProgrammaticToolCalling.integration.test.ts +319 -0
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +1006 -0
- package/src/tools/__tests__/ToolSearch.integration.test.ts +162 -0
- package/src/tools/__tests__/ToolSearch.test.ts +1003 -0
- package/src/tools/handlers.ts +363 -0
- package/src/tools/schema.ts +37 -0
- package/src/tools/search/anthropic.ts +51 -0
- package/src/tools/search/content.test.ts +173 -0
- package/src/tools/search/content.ts +147 -0
- package/src/tools/search/firecrawl.ts +210 -0
- package/src/tools/search/format.ts +250 -0
- package/src/tools/search/highlights.ts +320 -0
- package/src/tools/search/index.ts +3 -0
- package/src/tools/search/jina-reranker.test.ts +130 -0
- package/src/tools/search/output.md +2775 -0
- package/src/tools/search/rerankers.ts +242 -0
- package/src/tools/search/schema.ts +113 -0
- package/src/tools/search/search.ts +768 -0
- package/src/tools/search/serper-scraper.ts +155 -0
- package/src/tools/search/test.html +884 -0
- package/src/tools/search/test.md +643 -0
- package/src/tools/search/test.ts +159 -0
- package/src/tools/search/tool.ts +657 -0
- package/src/tools/search/types.ts +665 -0
- package/src/tools/search/utils.ts +79 -0
- package/src/types/graph.test.ts +218 -0
- package/src/types/graph.ts +533 -0
- package/src/types/index.ts +6 -0
- package/src/types/llm.ts +140 -0
- package/src/types/messages.ts +4 -0
- package/src/types/run.ts +128 -0
- package/src/types/stream.ts +417 -0
- package/src/types/tools.ts +355 -0
- package/src/utils/contextAnalytics.ts +103 -0
- package/src/utils/events.ts +32 -0
- package/src/utils/graph.ts +11 -0
- package/src/utils/handlers.ts +107 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/llm.ts +26 -0
- package/src/utils/llmConfig.ts +208 -0
- package/src/utils/logging.ts +48 -0
- package/src/utils/misc.ts +57 -0
- package/src/utils/run.ts +106 -0
- package/src/utils/schema.ts +35 -0
- package/src/utils/title.ts +177 -0
- package/src/utils/tokens.ts +142 -0
- package/src/utils/toonFormat.ts +475 -0
|
@@ -0,0 +1,809 @@
|
|
|
1
|
+
/* eslint-disable no-process-env */
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
|
+
/**
|
|
5
|
+
* Integration tests for structured output with real LLM API calls.
|
|
6
|
+
*
|
|
7
|
+
* These tests require actual API credentials. They will be skipped if
|
|
8
|
+
* the required environment variables are not set.
|
|
9
|
+
*
|
|
10
|
+
* To run:
|
|
11
|
+
* 1. Copy ranger/.env credentials to agents/.env (or set env vars directly)
|
|
12
|
+
* 2. npm test -- --testPathPattern=structured-output.integration
|
|
13
|
+
*
|
|
14
|
+
* Environment variables needed:
|
|
15
|
+
* - BEDROCK_AWS_ACCESS_KEY_ID + BEDROCK_AWS_SECRET_ACCESS_KEY + BEDROCK_AWS_DEFAULT_REGION
|
|
16
|
+
* - ANTHROPIC_API_KEY (optional, for direct Anthropic tests)
|
|
17
|
+
* - OPENAI_API_KEY (optional, for direct OpenAI tests)
|
|
18
|
+
*/
|
|
19
|
+
import { config } from 'dotenv';
|
|
20
|
+
import { resolve } from 'path';
|
|
21
|
+
|
|
22
|
+
// Load from ranger/.env if agents/.env doesn't exist
|
|
23
|
+
config(); // Try local first
|
|
24
|
+
config({ path: resolve(__dirname, '../../../../ranger/.env') }); // Then ranger
|
|
25
|
+
|
|
26
|
+
import { HumanMessage, SystemMessage, AIMessageChunk } from '@langchain/core/messages';
|
|
27
|
+
import { CustomChatBedrockConverse } from '@/llm/bedrock';
|
|
28
|
+
import { Providers } from '@/common';
|
|
29
|
+
import { validateStructuredOutput, prepareSchemaForProvider } from '@/schemas/validate';
|
|
30
|
+
|
|
31
|
+
jest.setTimeout(120000);
|
|
32
|
+
|
|
33
|
+
// ──────────────────────────────────────────────────────────────
|
|
34
|
+
// Helper: check if credentials are available
|
|
35
|
+
// ──────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
const hasBedrock =
|
|
38
|
+
!!process.env.BEDROCK_AWS_ACCESS_KEY_ID &&
|
|
39
|
+
!!process.env.BEDROCK_AWS_SECRET_ACCESS_KEY;
|
|
40
|
+
|
|
41
|
+
const hasAnthropic = !!process.env.ANTHROPIC_API_KEY;
|
|
42
|
+
const hasOpenAI = !!process.env.OPENAI_API_KEY;
|
|
43
|
+
|
|
44
|
+
// ──────────────────────────────────────────────────────────────
|
|
45
|
+
// Shared test schema
|
|
46
|
+
// ──────────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
const personSchema = {
|
|
49
|
+
type: 'object' as const,
|
|
50
|
+
properties: {
|
|
51
|
+
name: { type: 'string', description: 'Full name of the person' },
|
|
52
|
+
age: { type: 'number', description: 'Age in years' },
|
|
53
|
+
occupation: { type: 'string', description: 'Job title or occupation' },
|
|
54
|
+
},
|
|
55
|
+
required: ['name', 'age', 'occupation'],
|
|
56
|
+
additionalProperties: false,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const classificationSchema = {
|
|
60
|
+
type: 'object' as const,
|
|
61
|
+
properties: {
|
|
62
|
+
category: {
|
|
63
|
+
type: 'string',
|
|
64
|
+
enum: ['positive', 'negative', 'neutral'],
|
|
65
|
+
description: 'Sentiment category',
|
|
66
|
+
},
|
|
67
|
+
confidence: {
|
|
68
|
+
type: 'number',
|
|
69
|
+
description: 'Confidence score between 0 and 1',
|
|
70
|
+
},
|
|
71
|
+
reasoning: {
|
|
72
|
+
type: 'string',
|
|
73
|
+
description: 'Brief explanation of the classification',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
required: ['category', 'confidence', 'reasoning'],
|
|
77
|
+
additionalProperties: false,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// ──────────────────────────────────────────────────────────────
|
|
81
|
+
// Bedrock Integration Tests
|
|
82
|
+
// ──────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
describe('Bedrock structured output integration', () => {
|
|
85
|
+
const bedrockRegion = process.env.BEDROCK_AWS_DEFAULT_REGION || 'us-east-1';
|
|
86
|
+
|
|
87
|
+
const createBedrockModel = () =>
|
|
88
|
+
new CustomChatBedrockConverse({
|
|
89
|
+
// Use Claude 3 Haiku which is widely available and supports on-demand invocation
|
|
90
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
91
|
+
region: bedrockRegion,
|
|
92
|
+
credentials: {
|
|
93
|
+
accessKeyId: process.env.BEDROCK_AWS_ACCESS_KEY_ID!,
|
|
94
|
+
secretAccessKey: process.env.BEDROCK_AWS_SECRET_ACCESS_KEY!,
|
|
95
|
+
...(process.env.BEDROCK_AWS_SESSION_TOKEN
|
|
96
|
+
? { sessionToken: process.env.BEDROCK_AWS_SESSION_TOKEN }
|
|
97
|
+
: {}),
|
|
98
|
+
},
|
|
99
|
+
maxTokens: 1024,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const conditionalTest = hasBedrock ? test : test.skip;
|
|
103
|
+
|
|
104
|
+
conditionalTest(
|
|
105
|
+
'withStructuredOutput functionCalling - basic person extraction',
|
|
106
|
+
async () => {
|
|
107
|
+
const model = createBedrockModel();
|
|
108
|
+
const structuredModel = model.withStructuredOutput(personSchema, {
|
|
109
|
+
name: 'PersonInfo',
|
|
110
|
+
method: 'functionCalling' as any,
|
|
111
|
+
includeRaw: true,
|
|
112
|
+
strict: true,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const result = await structuredModel.invoke([
|
|
116
|
+
new SystemMessage('Extract person information from the text.'),
|
|
117
|
+
new HumanMessage('John Smith is a 35 year old software engineer from Seattle.'),
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
console.log('[Integration] Bedrock functionCalling result:', JSON.stringify(result.parsed ?? result, null, 2));
|
|
121
|
+
|
|
122
|
+
const parsed = result.parsed ?? result;
|
|
123
|
+
expect(parsed).toBeDefined();
|
|
124
|
+
expect(typeof parsed.name).toBe('string');
|
|
125
|
+
expect(typeof parsed.age).toBe('number');
|
|
126
|
+
expect(typeof parsed.occupation).toBe('string');
|
|
127
|
+
|
|
128
|
+
// Validate against schema
|
|
129
|
+
const validation = validateStructuredOutput(parsed, personSchema);
|
|
130
|
+
expect(validation.success).toBe(true);
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
conditionalTest(
|
|
135
|
+
'withStructuredOutput functionCalling - classification with enum',
|
|
136
|
+
async () => {
|
|
137
|
+
const model = createBedrockModel();
|
|
138
|
+
const structuredModel = model.withStructuredOutput(classificationSchema, {
|
|
139
|
+
name: 'Classification',
|
|
140
|
+
method: 'functionCalling' as any,
|
|
141
|
+
includeRaw: true,
|
|
142
|
+
strict: true,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const result = await structuredModel.invoke([
|
|
146
|
+
new SystemMessage('Classify the sentiment of the following text.'),
|
|
147
|
+
new HumanMessage('I absolutely love this product! It exceeded all my expectations.'),
|
|
148
|
+
]);
|
|
149
|
+
|
|
150
|
+
const parsed = result.parsed ?? result;
|
|
151
|
+
console.log('[Integration] Bedrock classification result:', JSON.stringify(parsed, null, 2));
|
|
152
|
+
|
|
153
|
+
expect(parsed).toBeDefined();
|
|
154
|
+
expect(['positive', 'negative', 'neutral']).toContain(parsed.category);
|
|
155
|
+
expect(typeof parsed.confidence).toBe('number');
|
|
156
|
+
expect(typeof parsed.reasoning).toBe('string');
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
conditionalTest(
|
|
161
|
+
'withStructuredOutput with prepared schema',
|
|
162
|
+
async () => {
|
|
163
|
+
const rawSchema = {
|
|
164
|
+
type: 'object',
|
|
165
|
+
properties: {
|
|
166
|
+
summary: { type: 'string', maxLength: 500 },
|
|
167
|
+
topics: { type: 'array', items: { type: 'string' }, minItems: 1, maxItems: 5 },
|
|
168
|
+
wordCount: { type: 'number', minimum: 0 },
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Prepare schema for Bedrock
|
|
173
|
+
const { schema: prepared, warnings } = prepareSchemaForProvider(
|
|
174
|
+
rawSchema,
|
|
175
|
+
Providers.BEDROCK,
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
console.log('[Integration] Schema preparation warnings:', warnings);
|
|
179
|
+
|
|
180
|
+
// Verify preparation worked
|
|
181
|
+
expect(prepared.additionalProperties).toBe(false);
|
|
182
|
+
expect(prepared.required).toEqual(
|
|
183
|
+
expect.arrayContaining(['summary', 'topics', 'wordCount']),
|
|
184
|
+
);
|
|
185
|
+
const summaryProp = (prepared.properties as any).summary;
|
|
186
|
+
expect(summaryProp.maxLength).toBeUndefined();
|
|
187
|
+
expect(summaryProp.description).toContain('maxLength');
|
|
188
|
+
|
|
189
|
+
// Now use the prepared schema with the model
|
|
190
|
+
const model = createBedrockModel();
|
|
191
|
+
const structuredModel = model.withStructuredOutput(prepared, {
|
|
192
|
+
name: 'Analysis',
|
|
193
|
+
method: 'functionCalling' as any,
|
|
194
|
+
includeRaw: true,
|
|
195
|
+
strict: true,
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const result = await structuredModel.invoke([
|
|
199
|
+
new SystemMessage('Analyze the following text and provide a summary, key topics, and word count.'),
|
|
200
|
+
new HumanMessage(
|
|
201
|
+
'Artificial intelligence has transformed the technology industry in recent years. ' +
|
|
202
|
+
'Machine learning models are now capable of generating text, images, and even code. ' +
|
|
203
|
+
'Companies are investing heavily in AI research and development.',
|
|
204
|
+
),
|
|
205
|
+
]);
|
|
206
|
+
|
|
207
|
+
const parsed = result.parsed ?? result;
|
|
208
|
+
console.log('[Integration] Bedrock analysis result:', JSON.stringify(parsed, null, 2));
|
|
209
|
+
|
|
210
|
+
expect(parsed).toBeDefined();
|
|
211
|
+
expect(typeof parsed.summary).toBe('string');
|
|
212
|
+
expect(Array.isArray(parsed.topics)).toBe(true);
|
|
213
|
+
expect(typeof parsed.wordCount).toBe('number');
|
|
214
|
+
},
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
conditionalTest(
|
|
218
|
+
'withStructuredOutput with tools - deferred structured output pattern',
|
|
219
|
+
async () => {
|
|
220
|
+
const model = createBedrockModel();
|
|
221
|
+
|
|
222
|
+
// Step 1: Regular invocation with tools (simulate tool use)
|
|
223
|
+
const messages = [
|
|
224
|
+
new SystemMessage(
|
|
225
|
+
'You are a helpful assistant. When asked to analyze text, provide a structured analysis.',
|
|
226
|
+
),
|
|
227
|
+
new HumanMessage(
|
|
228
|
+
'Analyze the sentiment: "This movie was absolutely terrible, worst I have ever seen."',
|
|
229
|
+
),
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
// Step 2: Get structured output (this simulates the deferred structured output pattern)
|
|
233
|
+
const structuredModel = model.withStructuredOutput(classificationSchema, {
|
|
234
|
+
name: 'SentimentAnalysis',
|
|
235
|
+
method: 'functionCalling' as any,
|
|
236
|
+
includeRaw: true,
|
|
237
|
+
strict: true,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const result = await structuredModel.invoke(messages);
|
|
241
|
+
|
|
242
|
+
const parsed = result.parsed ?? result;
|
|
243
|
+
console.log('[Integration] Bedrock deferred structured result:', JSON.stringify(parsed, null, 2));
|
|
244
|
+
|
|
245
|
+
expect(parsed).toBeDefined();
|
|
246
|
+
expect(['positive', 'negative', 'neutral']).toContain(parsed.category);
|
|
247
|
+
// The movie review is clearly negative
|
|
248
|
+
expect(parsed.category).toBe('negative');
|
|
249
|
+
expect(typeof parsed.confidence).toBe('number');
|
|
250
|
+
},
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
conditionalTest(
|
|
254
|
+
'validates response against schema and detects failures',
|
|
255
|
+
async () => {
|
|
256
|
+
// Test schema validation on a known good response
|
|
257
|
+
const goodResponse = {
|
|
258
|
+
name: 'Alice',
|
|
259
|
+
age: 30,
|
|
260
|
+
occupation: 'Engineer',
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const goodResult = validateStructuredOutput(goodResponse, personSchema);
|
|
264
|
+
expect(goodResult.success).toBe(true);
|
|
265
|
+
|
|
266
|
+
// Test with a bad response
|
|
267
|
+
const badResponse = {
|
|
268
|
+
name: 123, // Wrong type
|
|
269
|
+
age: 'thirty', // Wrong type
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const badResult = validateStructuredOutput(badResponse, personSchema);
|
|
273
|
+
expect(badResult.success).toBe(false);
|
|
274
|
+
expect(badResult.error).toBeDefined();
|
|
275
|
+
},
|
|
276
|
+
);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// ──────────────────────────────────────────────────────────────
|
|
280
|
+
// Complex Tool + Structured Output Integration Tests (Bedrock)
|
|
281
|
+
// ──────────────────────────────────────────────────────────────
|
|
282
|
+
|
|
283
|
+
describe('Bedrock complex tool + structured output integration', () => {
|
|
284
|
+
const bedrockRegion = process.env.BEDROCK_AWS_DEFAULT_REGION || 'us-east-1';
|
|
285
|
+
const conditionalTest = hasBedrock ? test : test.skip;
|
|
286
|
+
|
|
287
|
+
const createBedrockModel = () =>
|
|
288
|
+
new CustomChatBedrockConverse({
|
|
289
|
+
model: 'anthropic.claude-3-haiku-20240307-v1:0',
|
|
290
|
+
region: bedrockRegion,
|
|
291
|
+
credentials: {
|
|
292
|
+
accessKeyId: process.env.BEDROCK_AWS_ACCESS_KEY_ID!,
|
|
293
|
+
secretAccessKey: process.env.BEDROCK_AWS_SECRET_ACCESS_KEY!,
|
|
294
|
+
...(process.env.BEDROCK_AWS_SESSION_TOKEN
|
|
295
|
+
? { sessionToken: process.env.BEDROCK_AWS_SESSION_TOKEN }
|
|
296
|
+
: {}),
|
|
297
|
+
},
|
|
298
|
+
maxTokens: 2048,
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
conditionalTest(
|
|
302
|
+
'complex schema with nested objects and arrays - multi-step analysis',
|
|
303
|
+
async () => {
|
|
304
|
+
const complexSchema = {
|
|
305
|
+
type: 'object' as const,
|
|
306
|
+
properties: {
|
|
307
|
+
analysis: {
|
|
308
|
+
type: 'object',
|
|
309
|
+
properties: {
|
|
310
|
+
overall_sentiment: {
|
|
311
|
+
type: 'string',
|
|
312
|
+
enum: ['very_positive', 'positive', 'neutral', 'negative', 'very_negative'],
|
|
313
|
+
},
|
|
314
|
+
confidence: { type: 'number' },
|
|
315
|
+
key_themes: {
|
|
316
|
+
type: 'array',
|
|
317
|
+
items: {
|
|
318
|
+
type: 'object',
|
|
319
|
+
properties: {
|
|
320
|
+
theme: { type: 'string' },
|
|
321
|
+
sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
|
|
322
|
+
evidence: { type: 'string' },
|
|
323
|
+
},
|
|
324
|
+
required: ['theme', 'sentiment', 'evidence'],
|
|
325
|
+
additionalProperties: false,
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
required: ['overall_sentiment', 'confidence', 'key_themes'],
|
|
330
|
+
additionalProperties: false,
|
|
331
|
+
},
|
|
332
|
+
entities: {
|
|
333
|
+
type: 'array',
|
|
334
|
+
items: {
|
|
335
|
+
type: 'object',
|
|
336
|
+
properties: {
|
|
337
|
+
name: { type: 'string' },
|
|
338
|
+
type: { type: 'string', enum: ['person', 'organization', 'location', 'product'] },
|
|
339
|
+
role: { type: 'string' },
|
|
340
|
+
},
|
|
341
|
+
required: ['name', 'type', 'role'],
|
|
342
|
+
additionalProperties: false,
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
summary: { type: 'string' },
|
|
346
|
+
action_items: {
|
|
347
|
+
type: 'array',
|
|
348
|
+
items: { type: 'string' },
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
required: ['analysis', 'entities', 'summary', 'action_items'],
|
|
352
|
+
additionalProperties: false,
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const model = createBedrockModel();
|
|
356
|
+
const structuredModel = model.withStructuredOutput(complexSchema, {
|
|
357
|
+
name: 'DetailedAnalysis',
|
|
358
|
+
method: 'functionCalling' as any,
|
|
359
|
+
includeRaw: true,
|
|
360
|
+
strict: true,
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const result = await structuredModel.invoke([
|
|
364
|
+
new SystemMessage(
|
|
365
|
+
'You are an expert analyst. Analyze the following business communication and extract detailed structured information.',
|
|
366
|
+
),
|
|
367
|
+
new HumanMessage(
|
|
368
|
+
'From: Sarah Johnson, VP of Engineering at TechCorp\n' +
|
|
369
|
+
'To: All Engineering Teams\n\n' +
|
|
370
|
+
'I am pleased to announce that our new AI platform, CodeAssist, has exceeded Q3 targets by 45%. ' +
|
|
371
|
+
'The Seattle development team, led by Marcus Chen, deserves special recognition for their outstanding work. ' +
|
|
372
|
+
'However, we need to address the performance issues reported by our European clients. ' +
|
|
373
|
+
'The London office has flagged latency problems that must be resolved before the Q4 launch. ' +
|
|
374
|
+
'Action items: 1) Marcus to lead a performance task force. 2) Schedule a meeting with the London team. ' +
|
|
375
|
+
'3) Update the deployment pipeline for EU regions.',
|
|
376
|
+
),
|
|
377
|
+
]);
|
|
378
|
+
|
|
379
|
+
const parsed = result.parsed ?? result;
|
|
380
|
+
console.log('[Integration] Complex analysis result:', JSON.stringify(parsed, null, 2));
|
|
381
|
+
|
|
382
|
+
// Verify top-level structure
|
|
383
|
+
// Note: functionCalling mode is best-effort, so some fields may be missing.
|
|
384
|
+
// Native constrained decoding (when available) would guarantee all fields.
|
|
385
|
+
expect(parsed).toBeDefined();
|
|
386
|
+
expect(typeof parsed.summary).toBe('string');
|
|
387
|
+
|
|
388
|
+
// Analysis is the most complex nested field — verify if present
|
|
389
|
+
if (parsed.analysis) {
|
|
390
|
+
expect(['very_positive', 'positive', 'neutral', 'negative', 'very_negative']).toContain(
|
|
391
|
+
parsed.analysis.overall_sentiment,
|
|
392
|
+
);
|
|
393
|
+
expect(typeof parsed.analysis.confidence).toBe('number');
|
|
394
|
+
if (Array.isArray(parsed.analysis.key_themes)) {
|
|
395
|
+
expect(parsed.analysis.key_themes.length).toBeGreaterThan(0);
|
|
396
|
+
for (const theme of parsed.analysis.key_themes) {
|
|
397
|
+
expect(typeof theme.theme).toBe('string');
|
|
398
|
+
expect(['positive', 'negative', 'neutral']).toContain(theme.sentiment);
|
|
399
|
+
expect(typeof theme.evidence).toBe('string');
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Verify entities if present
|
|
405
|
+
if (Array.isArray(parsed.entities)) {
|
|
406
|
+
expect(parsed.entities.length).toBeGreaterThan(0);
|
|
407
|
+
for (const entity of parsed.entities) {
|
|
408
|
+
expect(typeof entity.name).toBe('string');
|
|
409
|
+
expect(['person', 'organization', 'location', 'product']).toContain(entity.type);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Verify action items
|
|
414
|
+
if (Array.isArray(parsed.action_items)) {
|
|
415
|
+
expect(parsed.action_items.length).toBeGreaterThan(0);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// At minimum, we expect the model returned something parseable with the key fields
|
|
419
|
+
expect(parsed.summary.length).toBeGreaterThan(0);
|
|
420
|
+
},
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
conditionalTest(
|
|
424
|
+
'tool-like multi-turn conversation with structured output at the end',
|
|
425
|
+
async () => {
|
|
426
|
+
// Simulate a multi-turn conversation where the agent has gathered info
|
|
427
|
+
// and now needs to produce structured output
|
|
428
|
+
const reportSchema = {
|
|
429
|
+
type: 'object' as const,
|
|
430
|
+
properties: {
|
|
431
|
+
report_title: { type: 'string' },
|
|
432
|
+
findings: {
|
|
433
|
+
type: 'array',
|
|
434
|
+
items: {
|
|
435
|
+
type: 'object',
|
|
436
|
+
properties: {
|
|
437
|
+
category: { type: 'string' },
|
|
438
|
+
description: { type: 'string' },
|
|
439
|
+
severity: { type: 'string', enum: ['low', 'medium', 'high', 'critical'] },
|
|
440
|
+
recommendation: { type: 'string' },
|
|
441
|
+
},
|
|
442
|
+
required: ['category', 'description', 'severity', 'recommendation'],
|
|
443
|
+
additionalProperties: false,
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
overall_risk_level: {
|
|
447
|
+
type: 'string',
|
|
448
|
+
enum: ['low', 'medium', 'high', 'critical'],
|
|
449
|
+
},
|
|
450
|
+
next_steps: {
|
|
451
|
+
type: 'array',
|
|
452
|
+
items: { type: 'string' },
|
|
453
|
+
},
|
|
454
|
+
},
|
|
455
|
+
required: ['report_title', 'findings', 'overall_risk_level', 'next_steps'],
|
|
456
|
+
additionalProperties: false,
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const model = createBedrockModel();
|
|
460
|
+
|
|
461
|
+
// Multi-turn conversation simulating tool results being fed in
|
|
462
|
+
const messages = [
|
|
463
|
+
new SystemMessage(
|
|
464
|
+
'You are a security analyst. You have performed a security audit and gathered the following data from various scans. ' +
|
|
465
|
+
'Produce a structured security report.',
|
|
466
|
+
),
|
|
467
|
+
new HumanMessage(
|
|
468
|
+
'Here are the scan results:\n\n' +
|
|
469
|
+
'Port Scan: Ports 22, 80, 443, 8080 are open. Port 8080 is running an outdated Apache Tomcat 8.5.\n\n' +
|
|
470
|
+
'Vulnerability Scan: Found SQL injection in /api/users endpoint. XSS vulnerability in search form. ' +
|
|
471
|
+
'Outdated OpenSSL 1.1.1 (should be 3.x). No rate limiting on login endpoint.\n\n' +
|
|
472
|
+
'Configuration Review: Default admin credentials found on Tomcat manager. ' +
|
|
473
|
+
'CORS is set to wildcard (*). No CSP headers. Cookies without HttpOnly flag.\n\n' +
|
|
474
|
+
'Provide your structured security report.',
|
|
475
|
+
),
|
|
476
|
+
];
|
|
477
|
+
|
|
478
|
+
const structuredModel = model.withStructuredOutput(reportSchema, {
|
|
479
|
+
name: 'SecurityReport',
|
|
480
|
+
method: 'functionCalling' as any,
|
|
481
|
+
includeRaw: true,
|
|
482
|
+
strict: true,
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
const result = await structuredModel.invoke(messages);
|
|
486
|
+
|
|
487
|
+
const parsed = result.parsed ?? result;
|
|
488
|
+
console.log('[Integration] Security report result:', JSON.stringify(parsed, null, 2));
|
|
489
|
+
|
|
490
|
+
// Verify structure
|
|
491
|
+
expect(typeof parsed.report_title).toBe('string');
|
|
492
|
+
expect(Array.isArray(parsed.findings)).toBe(true);
|
|
493
|
+
expect(parsed.findings.length).toBeGreaterThanOrEqual(3); // At least SQL injection, XSS, outdated software
|
|
494
|
+
expect(['low', 'medium', 'high', 'critical']).toContain(parsed.overall_risk_level);
|
|
495
|
+
expect(Array.isArray(parsed.next_steps)).toBe(true);
|
|
496
|
+
expect(parsed.next_steps.length).toBeGreaterThan(0);
|
|
497
|
+
|
|
498
|
+
// Verify each finding
|
|
499
|
+
for (const finding of parsed.findings) {
|
|
500
|
+
expect(typeof finding.category).toBe('string');
|
|
501
|
+
expect(typeof finding.description).toBe('string');
|
|
502
|
+
expect(['low', 'medium', 'high', 'critical']).toContain(finding.severity);
|
|
503
|
+
expect(typeof finding.recommendation).toBe('string');
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Given the scan results, risk should be high or critical
|
|
507
|
+
expect(['high', 'critical']).toContain(parsed.overall_risk_level);
|
|
508
|
+
|
|
509
|
+
// Validate entire response
|
|
510
|
+
const validation = validateStructuredOutput(parsed, reportSchema);
|
|
511
|
+
expect(validation.success).toBe(true);
|
|
512
|
+
},
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
conditionalTest(
|
|
516
|
+
'prepared schema with constraint stripping works end-to-end',
|
|
517
|
+
async () => {
|
|
518
|
+
// A schema with MANY unsupported constraints that need stripping
|
|
519
|
+
const rawSchema = {
|
|
520
|
+
type: 'object' as const,
|
|
521
|
+
properties: {
|
|
522
|
+
title: { type: 'string', minLength: 5, maxLength: 100 },
|
|
523
|
+
rating: { type: 'number', minimum: 1, maximum: 5, multipleOf: 0.5 },
|
|
524
|
+
tags: {
|
|
525
|
+
type: 'array',
|
|
526
|
+
items: { type: 'string', minLength: 1, maxLength: 50 },
|
|
527
|
+
minItems: 1,
|
|
528
|
+
maxItems: 5,
|
|
529
|
+
uniqueItems: true,
|
|
530
|
+
},
|
|
531
|
+
metadata: {
|
|
532
|
+
type: 'object',
|
|
533
|
+
properties: {
|
|
534
|
+
author: { type: 'string', pattern: '^[A-Za-z ]+$' },
|
|
535
|
+
word_count: { type: 'integer', minimum: 0, maximum: 100000 },
|
|
536
|
+
language: { type: 'string', enum: ['en', 'es', 'fr', 'de'] },
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
// Prepare for Bedrock
|
|
543
|
+
const { schema: prepared, warnings } = prepareSchemaForProvider(
|
|
544
|
+
rawSchema,
|
|
545
|
+
Providers.BEDROCK,
|
|
546
|
+
);
|
|
547
|
+
|
|
548
|
+
console.log('[Integration] Complex schema warnings count:', warnings.length);
|
|
549
|
+
expect(warnings.length).toBeGreaterThan(5); // Should have many warnings
|
|
550
|
+
|
|
551
|
+
// Verify ALL constraints were stripped
|
|
552
|
+
const titleProp = (prepared.properties as any).title;
|
|
553
|
+
expect(titleProp.minLength).toBeUndefined();
|
|
554
|
+
expect(titleProp.maxLength).toBeUndefined();
|
|
555
|
+
|
|
556
|
+
const ratingProp = (prepared.properties as any).rating;
|
|
557
|
+
expect(ratingProp.minimum).toBeUndefined();
|
|
558
|
+
expect(ratingProp.maximum).toBeUndefined();
|
|
559
|
+
expect(ratingProp.multipleOf).toBeUndefined();
|
|
560
|
+
|
|
561
|
+
const tagsProp = (prepared.properties as any).tags;
|
|
562
|
+
expect(tagsProp.minItems).toBeUndefined();
|
|
563
|
+
expect(tagsProp.maxItems).toBeUndefined();
|
|
564
|
+
expect(tagsProp.uniqueItems).toBeUndefined();
|
|
565
|
+
|
|
566
|
+
// Now use with actual model
|
|
567
|
+
const model = createBedrockModel();
|
|
568
|
+
const structuredModel = model.withStructuredOutput(prepared, {
|
|
569
|
+
name: 'BookReview',
|
|
570
|
+
method: 'functionCalling' as any,
|
|
571
|
+
includeRaw: true,
|
|
572
|
+
strict: true,
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
const result = await structuredModel.invoke([
|
|
576
|
+
new SystemMessage('You are a book reviewer. Review the book described below.'),
|
|
577
|
+
new HumanMessage(
|
|
578
|
+
'Review "The Great Gatsby" by F. Scott Fitzgerald. ' +
|
|
579
|
+
'It is a classic American novel written in English about the decline of the American Dream.',
|
|
580
|
+
),
|
|
581
|
+
]);
|
|
582
|
+
|
|
583
|
+
const parsed = result.parsed ?? result;
|
|
584
|
+
console.log('[Integration] Book review result:', JSON.stringify(parsed, null, 2));
|
|
585
|
+
|
|
586
|
+
expect(typeof parsed.title).toBe('string');
|
|
587
|
+
expect(typeof parsed.rating).toBe('number');
|
|
588
|
+
expect(Array.isArray(parsed.tags)).toBe(true);
|
|
589
|
+
expect(parsed.metadata).toBeDefined();
|
|
590
|
+
expect(['en', 'es', 'fr', 'de']).toContain(parsed.metadata.language);
|
|
591
|
+
},
|
|
592
|
+
);
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
// ──────────────────────────────────────────────────────────────
|
|
596
|
+
// Anthropic Direct Integration Tests
|
|
597
|
+
// ──────────────────────────────────────────────────────────────
|
|
598
|
+
|
|
599
|
+
describe('Anthropic direct structured output integration', () => {
|
|
600
|
+
const conditionalTest = hasAnthropic ? test : test.skip;
|
|
601
|
+
|
|
602
|
+
conditionalTest(
|
|
603
|
+
'withStructuredOutput jsonSchema (native) - person extraction',
|
|
604
|
+
async () => {
|
|
605
|
+
// Dynamic import to avoid errors when @langchain/anthropic is not installed
|
|
606
|
+
const { CustomAnthropic } = await import('@/llm/anthropic');
|
|
607
|
+
|
|
608
|
+
const model = new CustomAnthropic({
|
|
609
|
+
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
|
|
610
|
+
model: 'claude-sonnet-4-20250514',
|
|
611
|
+
maxTokens: 1024,
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
const structuredModel = model.withStructuredOutput(personSchema, {
|
|
615
|
+
name: 'PersonInfo',
|
|
616
|
+
method: 'jsonSchema' as any,
|
|
617
|
+
includeRaw: true,
|
|
618
|
+
strict: true,
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
const result = await structuredModel.invoke([
|
|
622
|
+
new SystemMessage('Extract person information from the text.'),
|
|
623
|
+
new HumanMessage('Marie Curie was a 66 year old physicist and chemist.'),
|
|
624
|
+
]);
|
|
625
|
+
|
|
626
|
+
const parsed = result.parsed ?? result;
|
|
627
|
+
console.log('[Integration] Anthropic native result:', JSON.stringify(parsed, null, 2));
|
|
628
|
+
|
|
629
|
+
expect(parsed).toBeDefined();
|
|
630
|
+
expect(typeof parsed.name).toBe('string');
|
|
631
|
+
expect(typeof parsed.age).toBe('number');
|
|
632
|
+
expect(typeof parsed.occupation).toBe('string');
|
|
633
|
+
|
|
634
|
+
const validation = validateStructuredOutput(parsed, personSchema);
|
|
635
|
+
expect(validation.success).toBe(true);
|
|
636
|
+
},
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
conditionalTest(
|
|
640
|
+
'withStructuredOutput jsonSchema - classification with enum',
|
|
641
|
+
async () => {
|
|
642
|
+
const { CustomAnthropic } = await import('@/llm/anthropic');
|
|
643
|
+
|
|
644
|
+
const model = new CustomAnthropic({
|
|
645
|
+
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
|
|
646
|
+
model: 'claude-sonnet-4-20250514',
|
|
647
|
+
maxTokens: 1024,
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
const structuredModel = model.withStructuredOutput(classificationSchema, {
|
|
651
|
+
name: 'Sentiment',
|
|
652
|
+
method: 'jsonSchema' as any,
|
|
653
|
+
includeRaw: true,
|
|
654
|
+
strict: true,
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
const result = await structuredModel.invoke([
|
|
658
|
+
new SystemMessage('Classify the sentiment.'),
|
|
659
|
+
new HumanMessage('This is the best day of my life!'),
|
|
660
|
+
]);
|
|
661
|
+
|
|
662
|
+
const parsed = result.parsed ?? result;
|
|
663
|
+
console.log('[Integration] Anthropic classification:', JSON.stringify(parsed, null, 2));
|
|
664
|
+
|
|
665
|
+
expect(['positive', 'negative', 'neutral']).toContain(parsed.category);
|
|
666
|
+
expect(parsed.category).toBe('positive');
|
|
667
|
+
},
|
|
668
|
+
);
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
// ──────────────────────────────────────────────────────────────
|
|
672
|
+
// OpenAI Integration Tests
|
|
673
|
+
// ──────────────────────────────────────────────────────────────
|
|
674
|
+
|
|
675
|
+
describe('OpenAI structured output integration', () => {
|
|
676
|
+
const conditionalTest = hasOpenAI ? test : test.skip;
|
|
677
|
+
|
|
678
|
+
conditionalTest(
|
|
679
|
+
'withStructuredOutput jsonSchema (native) - person extraction',
|
|
680
|
+
async () => {
|
|
681
|
+
const { ChatOpenAI } = await import('@/llm/openai');
|
|
682
|
+
|
|
683
|
+
const model = new ChatOpenAI({
|
|
684
|
+
openAIApiKey: process.env.OPENAI_API_KEY,
|
|
685
|
+
model: 'gpt-4o-mini',
|
|
686
|
+
maxTokens: 1024,
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
const structuredModel = model.withStructuredOutput(personSchema, {
|
|
690
|
+
name: 'PersonInfo',
|
|
691
|
+
method: 'jsonSchema' as any,
|
|
692
|
+
includeRaw: true,
|
|
693
|
+
strict: true,
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
const result = await structuredModel.invoke([
|
|
697
|
+
new SystemMessage('Extract person information from the text.'),
|
|
698
|
+
new HumanMessage('Albert Einstein was a 76 year old theoretical physicist.'),
|
|
699
|
+
]);
|
|
700
|
+
|
|
701
|
+
const parsed = result.parsed ?? result;
|
|
702
|
+
console.log('[Integration] OpenAI native result:', JSON.stringify(parsed, null, 2));
|
|
703
|
+
|
|
704
|
+
expect(parsed).toBeDefined();
|
|
705
|
+
expect(typeof parsed.name).toBe('string');
|
|
706
|
+
expect(typeof parsed.age).toBe('number');
|
|
707
|
+
|
|
708
|
+
const validation = validateStructuredOutput(parsed, personSchema);
|
|
709
|
+
expect(validation.success).toBe(true);
|
|
710
|
+
},
|
|
711
|
+
);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
// ──────────────────────────────────────────────────────────────
|
|
715
|
+
// Schema Preparation Integration Tests
|
|
716
|
+
// ──────────────────────────────────────────────────────────────
|
|
717
|
+
|
|
718
|
+
describe('Schema preparation produces valid schemas for all providers', () => {
|
|
719
|
+
const complexSchema = {
|
|
720
|
+
type: 'object',
|
|
721
|
+
properties: {
|
|
722
|
+
analysis: {
|
|
723
|
+
type: 'object',
|
|
724
|
+
properties: {
|
|
725
|
+
score: { type: 'number', minimum: 0, maximum: 100 },
|
|
726
|
+
label: { type: 'string', enum: ['low', 'medium', 'high'] },
|
|
727
|
+
details: { type: 'string', maxLength: 1000 },
|
|
728
|
+
},
|
|
729
|
+
},
|
|
730
|
+
tags: {
|
|
731
|
+
type: 'array',
|
|
732
|
+
items: { type: 'string', minLength: 1 },
|
|
733
|
+
minItems: 1,
|
|
734
|
+
maxItems: 10,
|
|
735
|
+
},
|
|
736
|
+
metadata: {
|
|
737
|
+
type: 'object',
|
|
738
|
+
properties: {
|
|
739
|
+
source: { type: 'string' },
|
|
740
|
+
timestamp: { type: 'string' },
|
|
741
|
+
},
|
|
742
|
+
},
|
|
743
|
+
},
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
test('prepares valid schema for Anthropic', () => {
|
|
747
|
+
const { schema, warnings } = prepareSchemaForProvider(
|
|
748
|
+
complexSchema,
|
|
749
|
+
Providers.ANTHROPIC,
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
// Root object
|
|
753
|
+
expect(schema.additionalProperties).toBe(false);
|
|
754
|
+
expect(schema.required).toEqual(
|
|
755
|
+
expect.arrayContaining(['analysis', 'tags', 'metadata']),
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
// Nested object: analysis
|
|
759
|
+
const analysis = (schema.properties as any).analysis;
|
|
760
|
+
expect(analysis.additionalProperties).toBe(false);
|
|
761
|
+
expect(analysis.required).toEqual(
|
|
762
|
+
expect.arrayContaining(['score', 'label', 'details']),
|
|
763
|
+
);
|
|
764
|
+
|
|
765
|
+
// Numeric constraints stripped
|
|
766
|
+
expect(analysis.properties.score.minimum).toBeUndefined();
|
|
767
|
+
expect(analysis.properties.score.maximum).toBeUndefined();
|
|
768
|
+
|
|
769
|
+
// String constraints stripped
|
|
770
|
+
expect(analysis.properties.details.maxLength).toBeUndefined();
|
|
771
|
+
|
|
772
|
+
// Array constraints stripped
|
|
773
|
+
const tags = (schema.properties as any).tags;
|
|
774
|
+
expect(tags.minItems).toBeUndefined();
|
|
775
|
+
expect(tags.maxItems).toBeUndefined();
|
|
776
|
+
|
|
777
|
+
// Array item string constraints stripped
|
|
778
|
+
expect(tags.items.minLength).toBeUndefined();
|
|
779
|
+
|
|
780
|
+
// Nested object: metadata
|
|
781
|
+
const metadata = (schema.properties as any).metadata;
|
|
782
|
+
expect(metadata.additionalProperties).toBe(false);
|
|
783
|
+
|
|
784
|
+
expect(warnings.length).toBeGreaterThan(0);
|
|
785
|
+
console.log('[Integration] Anthropic schema warnings:', warnings);
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
test('prepares valid schema for OpenAI', () => {
|
|
789
|
+
const { schema } = prepareSchemaForProvider(
|
|
790
|
+
complexSchema,
|
|
791
|
+
Providers.OPENAI,
|
|
792
|
+
);
|
|
793
|
+
|
|
794
|
+
expect(schema.additionalProperties).toBe(false);
|
|
795
|
+
const analysis = (schema.properties as any).analysis;
|
|
796
|
+
expect(analysis.properties.score.minimum).toBeUndefined();
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
test('prepares valid schema for Bedrock', () => {
|
|
800
|
+
const { schema } = prepareSchemaForProvider(
|
|
801
|
+
complexSchema,
|
|
802
|
+
Providers.BEDROCK,
|
|
803
|
+
);
|
|
804
|
+
|
|
805
|
+
expect(schema.additionalProperties).toBe(false);
|
|
806
|
+
const analysis = (schema.properties as any).analysis;
|
|
807
|
+
expect(analysis.properties.score.minimum).toBeUndefined();
|
|
808
|
+
});
|
|
809
|
+
});
|