@od-oneapp/ai-platform 0.1.0
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 +235 -0
- package/dist/agents-base.d.mts +12 -0
- package/dist/agents-base.d.mts.map +1 -0
- package/dist/agents-base.mjs +20 -0
- package/dist/agents-base.mjs.map +1 -0
- package/dist/agents-control-flow.d.mts +115 -0
- package/dist/agents-control-flow.d.mts.map +1 -0
- package/dist/agents-control-flow.mjs +514 -0
- package/dist/agents-control-flow.mjs.map +1 -0
- package/dist/agents-coordinator.d.mts +11 -0
- package/dist/agents-coordinator.d.mts.map +1 -0
- package/dist/agents-coordinator.mjs +12 -0
- package/dist/agents-coordinator.mjs.map +1 -0
- package/dist/agents-experimental.d.mts +153 -0
- package/dist/agents-experimental.d.mts.map +1 -0
- package/dist/agents-experimental.mjs +580 -0
- package/dist/agents-experimental.mjs.map +1 -0
- package/dist/agents-fallback.d.mts +10 -0
- package/dist/agents-fallback.d.mts.map +1 -0
- package/dist/agents-fallback.mjs +15 -0
- package/dist/agents-fallback.mjs.map +1 -0
- package/dist/agents-multi-swarm.d.mts +9 -0
- package/dist/agents-multi-swarm.d.mts.map +1 -0
- package/dist/agents-multi-swarm.mjs +44 -0
- package/dist/agents-multi-swarm.mjs.map +1 -0
- package/dist/agents-multi.d.mts +10 -0
- package/dist/agents-multi.d.mts.map +1 -0
- package/dist/agents-multi.mjs +44 -0
- package/dist/agents-multi.mjs.map +1 -0
- package/dist/agents-observability.d.mts +161 -0
- package/dist/agents-observability.d.mts.map +1 -0
- package/dist/agents-observability.mjs +550 -0
- package/dist/agents-observability.mjs.map +1 -0
- package/dist/agents-patterns.d.mts +9 -0
- package/dist/agents-patterns.d.mts.map +1 -0
- package/dist/agents-patterns.mjs +18 -0
- package/dist/agents-patterns.mjs.map +1 -0
- package/dist/agents-persistence.d.mts +234 -0
- package/dist/agents-persistence.d.mts.map +1 -0
- package/dist/agents-persistence.mjs +447 -0
- package/dist/agents-persistence.mjs.map +1 -0
- package/dist/agents-triage.d.mts +11 -0
- package/dist/agents-triage.d.mts.map +1 -0
- package/dist/agents-triage.mjs +13 -0
- package/dist/agents-triage.mjs.map +1 -0
- package/dist/agents-workflows.d.mts +9 -0
- package/dist/agents-workflows.d.mts.map +1 -0
- package/dist/agents-workflows.mjs +9 -0
- package/dist/agents-workflows.mjs.map +1 -0
- package/dist/agents.d.mts +30 -0
- package/dist/agents.d.mts.map +1 -0
- package/dist/agents.mjs +50 -0
- package/dist/agents.mjs.map +1 -0
- package/dist/aggregation-8KJF1uzp.d.mts +276 -0
- package/dist/aggregation-8KJF1uzp.d.mts.map +1 -0
- package/dist/aggregation-BDop87kL.mjs +1180 -0
- package/dist/aggregation-BDop87kL.mjs.map +1 -0
- package/dist/ai-runtime-CDzQztTt.mjs +85 -0
- package/dist/ai-runtime-CDzQztTt.mjs.map +1 -0
- package/dist/ai-runtime-DIwOEc6g.d.mts +56 -0
- package/dist/ai-runtime-DIwOEc6g.d.mts.map +1 -0
- package/dist/ai-sdk-error-integration-D0GDqrM0.d.mts +553 -0
- package/dist/ai-sdk-error-integration-D0GDqrM0.d.mts.map +1 -0
- package/dist/approval-queue-BcDDQ4oQ.mjs +104 -0
- package/dist/approval-queue-BcDDQ4oQ.mjs.map +1 -0
- package/dist/approval-queue-CiKiFT9z.d.mts +21 -0
- package/dist/approval-queue-CiKiFT9z.d.mts.map +1 -0
- package/dist/audio-BzvN7r10.d.mts +79 -0
- package/dist/audio-BzvN7r10.d.mts.map +1 -0
- package/dist/audio-vBG_62ME.mjs +226 -0
- package/dist/audio-vBG_62ME.mjs.map +1 -0
- package/dist/audit-logger-Bb2JIcIk.d.mts +12 -0
- package/dist/audit-logger-Bb2JIcIk.d.mts.map +1 -0
- package/dist/audit-logger-CHIP8bRO.mjs +596 -0
- package/dist/audit-logger-CHIP8bRO.mjs.map +1 -0
- package/dist/auto-resume-BpUNbPtp.d.mts +160 -0
- package/dist/auto-resume-BpUNbPtp.d.mts.map +1 -0
- package/dist/auto-resume-BuFRNvAX.mjs +638 -0
- package/dist/auto-resume-BuFRNvAX.mjs.map +1 -0
- package/dist/budget-guard-C83KCH9V.d.mts +52 -0
- package/dist/budget-guard-C83KCH9V.d.mts.map +1 -0
- package/dist/budget-guard-d_b5rq4u.mjs +158 -0
- package/dist/budget-guard-d_b5rq4u.mjs.map +1 -0
- package/dist/budget-guard-gyhJS00s.mjs +234 -0
- package/dist/budget-guard-gyhJS00s.mjs.map +1 -0
- package/dist/buffer-BC8mvXHE.d.mts +98 -0
- package/dist/buffer-BC8mvXHE.d.mts.map +1 -0
- package/dist/buffer-CefJGbRy.mjs +289 -0
- package/dist/buffer-CefJGbRy.mjs.map +1 -0
- package/dist/caching-adapters.d.mts +5 -0
- package/dist/caching-adapters.mjs +3 -0
- package/dist/caching-strategies.d.mts +52 -0
- package/dist/caching-strategies.d.mts.map +1 -0
- package/dist/caching-strategies.mjs +703 -0
- package/dist/caching-strategies.mjs.map +1 -0
- package/dist/caching.d.mts +14 -0
- package/dist/caching.d.mts.map +1 -0
- package/dist/caching.mjs +15 -0
- package/dist/caching.mjs.map +1 -0
- package/dist/catalog.d.mts +19 -0
- package/dist/catalog.d.mts.map +1 -0
- package/dist/catalog.mjs +1114 -0
- package/dist/catalog.mjs.map +1 -0
- package/dist/chunk-CkzbjWQW.mjs +20 -0
- package/dist/circuit-breaker-DoKWPORd.mjs +262 -0
- package/dist/circuit-breaker-DoKWPORd.mjs.map +1 -0
- package/dist/circuit-breaker-Mey3E7tW.d.mts +64 -0
- package/dist/circuit-breaker-Mey3E7tW.d.mts.map +1 -0
- package/dist/citation-generator-C-9RpbHq.mjs +103 -0
- package/dist/citation-generator-C-9RpbHq.mjs.map +1 -0
- package/dist/citation-generator-CDSymDs_.d.mts +18 -0
- package/dist/citation-generator-CDSymDs_.d.mts.map +1 -0
- package/dist/client-CpacYDIE.mjs +882 -0
- package/dist/client-CpacYDIE.mjs.map +1 -0
- package/dist/client.d.mts +103 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +470 -0
- package/dist/client.mjs.map +1 -0
- package/dist/compliance-approval-queue-DQGLojAm.mjs +172 -0
- package/dist/compliance-approval-queue-DQGLojAm.mjs.map +1 -0
- package/dist/compliance-approval-queue-IrMxFfSJ.d.mts +99 -0
- package/dist/compliance-approval-queue-IrMxFfSJ.d.mts.map +1 -0
- package/dist/compliance-wrapper-CrOMHhHN.mjs +528 -0
- package/dist/compliance-wrapper-CrOMHhHN.mjs.map +1 -0
- package/dist/conditions-DmQ6Y1Wt.mjs +179 -0
- package/dist/conditions-DmQ6Y1Wt.mjs.map +1 -0
- package/dist/conditions-zDrKfrc3.d.mts +42 -0
- package/dist/conditions-zDrKfrc3.d.mts.map +1 -0
- package/dist/console-BGMxxPZN.mjs +181 -0
- package/dist/console-BGMxxPZN.mjs.map +1 -0
- package/dist/console-DqEqZd4A.d.mts +76 -0
- package/dist/console-DqEqZd4A.d.mts.map +1 -0
- package/dist/controller-BOy3-xbC.mjs +501 -0
- package/dist/controller-BOy3-xbC.mjs.map +1 -0
- package/dist/controller-Y0NGosbJ.d.mts +104 -0
- package/dist/controller-Y0NGosbJ.d.mts.map +1 -0
- package/dist/coordinator-agent-BglqZLwo.d.mts +54 -0
- package/dist/coordinator-agent-BglqZLwo.d.mts.map +1 -0
- package/dist/coordinator-agent-WFWBRL-G.mjs +236 -0
- package/dist/coordinator-agent-WFWBRL-G.mjs.map +1 -0
- package/dist/crypto-8ABhc3TD.mjs +40 -0
- package/dist/crypto-8ABhc3TD.mjs.map +1 -0
- package/dist/environment-CSoJb0SW.mjs +255 -0
- package/dist/environment-CSoJb0SW.mjs.map +1 -0
- package/dist/error-handling-DNVkm6RY.mjs +1334 -0
- package/dist/error-handling-DNVkm6RY.mjs.map +1 -0
- package/dist/errors-CQ8tF4dP.mjs +985 -0
- package/dist/errors-CQ8tF4dP.mjs.map +1 -0
- package/dist/errors-CfYdVeum.d.mts +212 -0
- package/dist/errors-CfYdVeum.d.mts.map +1 -0
- package/dist/errors-Dtn-UeRi.mjs +61 -0
- package/dist/errors-Dtn-UeRi.mjs.map +1 -0
- package/dist/evaluator-Cs84qkr8.mjs +91 -0
- package/dist/evaluator-Cs84qkr8.mjs.map +1 -0
- package/dist/evaluator-optimizer-De67_mJC.mjs +1086 -0
- package/dist/evaluator-optimizer-De67_mJC.mjs.map +1 -0
- package/dist/evaluator-optimizer-pattern-B5939s2Z.mjs +367 -0
- package/dist/evaluator-optimizer-pattern-B5939s2Z.mjs.map +1 -0
- package/dist/evaluator-optimizer-pattern-D1AJrzBD.d.mts +72 -0
- package/dist/evaluator-optimizer-pattern-D1AJrzBD.d.mts.map +1 -0
- package/dist/factory-DP6VSl8C.mjs +307 -0
- package/dist/factory-DP6VSl8C.mjs.map +1 -0
- package/dist/generative-ui-catalog.d.mts +8 -0
- package/dist/generative-ui-catalog.d.mts.map +1 -0
- package/dist/generative-ui-catalog.mjs +679 -0
- package/dist/generative-ui-catalog.mjs.map +1 -0
- package/dist/generative-ui-registry.d.mts +195 -0
- package/dist/generative-ui-registry.d.mts.map +1 -0
- package/dist/generative-ui-registry.mjs +250 -0
- package/dist/generative-ui-registry.mjs.map +1 -0
- package/dist/generative-ui-stream.d.mts +23 -0
- package/dist/generative-ui-stream.d.mts.map +1 -0
- package/dist/generative-ui-stream.mjs +219 -0
- package/dist/generative-ui-stream.mjs.map +1 -0
- package/dist/generative-ui-types.d.mts +123 -0
- package/dist/generative-ui-types.d.mts.map +1 -0
- package/dist/generative-ui-types.mjs +1 -0
- package/dist/generative-ui.d.mts +13 -0
- package/dist/generative-ui.d.mts.map +1 -0
- package/dist/generative-ui.mjs +21 -0
- package/dist/generative-ui.mjs.map +1 -0
- package/dist/governance-audit.d.mts +3 -0
- package/dist/governance-audit.mjs +3 -0
- package/dist/governance-compliance.d.mts +5 -0
- package/dist/governance-compliance.mjs +4 -0
- package/dist/governance-policies.d.mts +4 -0
- package/dist/governance-policies.mjs +4 -0
- package/dist/governance-tenancy.d.mts +3 -0
- package/dist/governance-tenancy.mjs +3 -0
- package/dist/governance.d.mts +88 -0
- package/dist/governance.d.mts.map +1 -0
- package/dist/governance.mjs +432 -0
- package/dist/governance.mjs.map +1 -0
- package/dist/grounding-attribution.d.mts +63 -0
- package/dist/grounding-attribution.d.mts.map +1 -0
- package/dist/grounding-attribution.mjs +259 -0
- package/dist/grounding-attribution.mjs.map +1 -0
- package/dist/grounding-citation.d.mts +2 -0
- package/dist/grounding-citation.mjs +3 -0
- package/dist/grounding-context.d.mts +9 -0
- package/dist/grounding-context.d.mts.map +1 -0
- package/dist/grounding-context.mjs +19 -0
- package/dist/grounding-context.mjs.map +1 -0
- package/dist/grounding-embed.d.mts +102 -0
- package/dist/grounding-embed.d.mts.map +1 -0
- package/dist/grounding-embed.mjs +417 -0
- package/dist/grounding-embed.mjs.map +1 -0
- package/dist/grounding-hallucination.d.mts +44 -0
- package/dist/grounding-hallucination.d.mts.map +1 -0
- package/dist/grounding-hallucination.mjs +115 -0
- package/dist/grounding-hallucination.mjs.map +1 -0
- package/dist/grounding-proof-map.d.mts +9 -0
- package/dist/grounding-proof-map.d.mts.map +1 -0
- package/dist/grounding-proof-map.mjs +26 -0
- package/dist/grounding-proof-map.mjs.map +1 -0
- package/dist/grounding-rag.d.mts +10 -0
- package/dist/grounding-rag.d.mts.map +1 -0
- package/dist/grounding-rag.mjs +27 -0
- package/dist/grounding-rag.mjs.map +1 -0
- package/dist/grounding-verification.d.mts +48 -0
- package/dist/grounding-verification.d.mts.map +1 -0
- package/dist/grounding-verification.mjs +224 -0
- package/dist/grounding-verification.mjs.map +1 -0
- package/dist/grounding.d.mts +24 -0
- package/dist/grounding.d.mts.map +1 -0
- package/dist/grounding.mjs +77 -0
- package/dist/grounding.mjs.map +1 -0
- package/dist/hitl-active-learning.d.mts +41 -0
- package/dist/hitl-active-learning.d.mts.map +1 -0
- package/dist/hitl-active-learning.mjs +178 -0
- package/dist/hitl-active-learning.mjs.map +1 -0
- package/dist/hitl-annotation.d.mts +74 -0
- package/dist/hitl-annotation.d.mts.map +1 -0
- package/dist/hitl-annotation.mjs +170 -0
- package/dist/hitl-annotation.mjs.map +1 -0
- package/dist/hitl-approval.d.mts +2 -0
- package/dist/hitl-approval.mjs +3 -0
- package/dist/hitl-feedback.d.mts +59 -0
- package/dist/hitl-feedback.d.mts.map +1 -0
- package/dist/hitl-feedback.mjs +137 -0
- package/dist/hitl-feedback.mjs.map +1 -0
- package/dist/hitl-review.d.mts +2 -0
- package/dist/hitl-review.mjs +3 -0
- package/dist/hitl.d.mts +14 -0
- package/dist/hitl.d.mts.map +1 -0
- package/dist/hitl.mjs +22 -0
- package/dist/hitl.mjs.map +1 -0
- package/dist/index-B17HT-VL.d.mts +285 -0
- package/dist/index-B17HT-VL.d.mts.map +1 -0
- package/dist/index-BDwgsK9B.d.mts +101 -0
- package/dist/index-BDwgsK9B.d.mts.map +1 -0
- package/dist/index-BGgMn_Ev.d.mts +2615 -0
- package/dist/index-BGgMn_Ev.d.mts.map +1 -0
- package/dist/index-DOqe5r9G.d.mts +318 -0
- package/dist/index-DOqe5r9G.d.mts.map +1 -0
- package/dist/index-DotINT7o.d.mts +1004 -0
- package/dist/index-DotINT7o.d.mts.map +1 -0
- package/dist/index-URlW7aD1.d.mts +67 -0
- package/dist/index-URlW7aD1.d.mts.map +1 -0
- package/dist/index.d.mts +64 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +46 -0
- package/dist/index.mjs.map +1 -0
- package/dist/integrations-blob-storage.d.mts +25 -0
- package/dist/integrations-blob-storage.d.mts.map +1 -0
- package/dist/integrations-blob-storage.mjs +3 -0
- package/dist/integrations-notifications.d.mts +2 -0
- package/dist/integrations-notifications.mjs +3 -0
- package/dist/integrations-rate-limit.d.mts +27 -0
- package/dist/integrations-rate-limit.d.mts.map +1 -0
- package/dist/integrations-rate-limit.mjs +30 -0
- package/dist/integrations-rate-limit.mjs.map +1 -0
- package/dist/integrations-redis.d.mts +3 -0
- package/dist/integrations-redis.mjs +3 -0
- package/dist/integrations-stream.d.mts +14 -0
- package/dist/integrations-stream.d.mts.map +1 -0
- package/dist/integrations-stream.mjs +3 -0
- package/dist/integrations.d.mts +7 -0
- package/dist/integrations.mjs +7 -0
- package/dist/log-adapter-BLegSZtz.d.mts +16 -0
- package/dist/log-adapter-BLegSZtz.d.mts.map +1 -0
- package/dist/log-adapter-PPe_2Pwv.mjs +28 -0
- package/dist/log-adapter-PPe_2Pwv.mjs.map +1 -0
- package/dist/loop-BOYEtr2g.mjs +520 -0
- package/dist/loop-BOYEtr2g.mjs.map +1 -0
- package/dist/loop-C-tBBOqi.d.mts +219 -0
- package/dist/loop-C-tBBOqi.d.mts.map +1 -0
- package/dist/middleware-5wQ9bANW.mjs +306 -0
- package/dist/middleware-5wQ9bANW.mjs.map +1 -0
- package/dist/middleware-C1apSrj0.mjs +613 -0
- package/dist/middleware-C1apSrj0.mjs.map +1 -0
- package/dist/middleware-C7k0uItW.d.mts +9 -0
- package/dist/middleware-C7k0uItW.d.mts.map +1 -0
- package/dist/middleware-CZQCTHfl.mjs +366 -0
- package/dist/middleware-CZQCTHfl.mjs.map +1 -0
- package/dist/models.d.mts +11 -0
- package/dist/models.d.mts.map +1 -0
- package/dist/models.mjs +32 -0
- package/dist/models.mjs.map +1 -0
- package/dist/observability-analytics.d.mts +107 -0
- package/dist/observability-analytics.d.mts.map +1 -0
- package/dist/observability-analytics.mjs +409 -0
- package/dist/observability-analytics.mjs.map +1 -0
- package/dist/observability-cost.d.mts +10 -0
- package/dist/observability-cost.d.mts.map +1 -0
- package/dist/observability-cost.mjs +15 -0
- package/dist/observability-cost.mjs.map +1 -0
- package/dist/observability-telemetry.d.mts +111 -0
- package/dist/observability-telemetry.d.mts.map +1 -0
- package/dist/observability-telemetry.mjs +343 -0
- package/dist/observability-telemetry.mjs.map +1 -0
- package/dist/observability-tracing.d.mts +10 -0
- package/dist/observability-tracing.d.mts.map +1 -0
- package/dist/observability-tracing.mjs +17 -0
- package/dist/observability-tracing.mjs.map +1 -0
- package/dist/observability.d.mts +15 -0
- package/dist/observability.d.mts.map +1 -0
- package/dist/observability.mjs +17 -0
- package/dist/observability.mjs.map +1 -0
- package/dist/optimizer-DhXXpci6.mjs +97 -0
- package/dist/optimizer-DhXXpci6.mjs.map +1 -0
- package/dist/output-multimodal.d.mts +9 -0
- package/dist/output-multimodal.d.mts.map +1 -0
- package/dist/output-multimodal.mjs +18 -0
- package/dist/output-multimodal.mjs.map +1 -0
- package/dist/output.d.mts +11 -0
- package/dist/output.d.mts.map +1 -0
- package/dist/output.mjs +40 -0
- package/dist/output.mjs.map +1 -0
- package/dist/pii-filter-3AxmYSiu.d.mts +23 -0
- package/dist/pii-filter-3AxmYSiu.d.mts.map +1 -0
- package/dist/pipelines.d.mts +448 -0
- package/dist/pipelines.d.mts.map +1 -0
- package/dist/pipelines.mjs +1534 -0
- package/dist/pipelines.mjs.map +1 -0
- package/dist/prompt-injection-DQXchzsV.d.mts +8 -0
- package/dist/prompt-injection-DQXchzsV.d.mts.map +1 -0
- package/dist/prompt-injection-RpoLPwSa.mjs +52 -0
- package/dist/prompt-injection-RpoLPwSa.mjs.map +1 -0
- package/dist/prompts.d.mts +192 -0
- package/dist/prompts.d.mts.map +1 -0
- package/dist/prompts.mjs +732 -0
- package/dist/prompts.mjs.map +1 -0
- package/dist/protocol-DfBiEsnl.d.mts +112 -0
- package/dist/protocol-DfBiEsnl.d.mts.map +1 -0
- package/dist/quota-manager-0iPMkQWN.d.mts +62 -0
- package/dist/quota-manager-0iPMkQWN.d.mts.map +1 -0
- package/dist/quota-manager-D_N7FuQ2.mjs +180 -0
- package/dist/quota-manager-D_N7FuQ2.mjs.map +1 -0
- package/dist/redaction-utils-DcQwsiNh.mjs +438 -0
- package/dist/redaction-utils-DcQwsiNh.mjs.map +1 -0
- package/dist/redis-CpsSrF8K.mjs +102 -0
- package/dist/redis-CpsSrF8K.mjs.map +1 -0
- package/dist/redis-CwguYFGh.d.mts +33 -0
- package/dist/redis-CwguYFGh.d.mts.map +1 -0
- package/dist/registry-CsD3iTIx.mjs +190 -0
- package/dist/registry-CsD3iTIx.mjs.map +1 -0
- package/dist/registry-DVPWzkXR.d.mts +36 -0
- package/dist/registry-DVPWzkXR.d.mts.map +1 -0
- package/dist/reranking-BpWYhYzl.d.mts +72 -0
- package/dist/reranking-BpWYhYzl.d.mts.map +1 -0
- package/dist/reranking-Ck8aKZW7.mjs +331 -0
- package/dist/reranking-Ck8aKZW7.mjs.map +1 -0
- package/dist/resumable-adapter-CO1HtsgJ.mjs +21 -0
- package/dist/resumable-adapter-CO1HtsgJ.mjs.map +1 -0
- package/dist/review-trigger-DmAsiQlM.d.mts +24 -0
- package/dist/review-trigger-DmAsiQlM.d.mts.map +1 -0
- package/dist/review-trigger-DwXfpww9.mjs +112 -0
- package/dist/review-trigger-DwXfpww9.mjs.map +1 -0
- package/dist/safe-context-BynhkTKR.d.mts +54 -0
- package/dist/safe-context-BynhkTKR.d.mts.map +1 -0
- package/dist/safe-context-C5A3Wv3b.mjs +143 -0
- package/dist/safe-context-C5A3Wv3b.mjs.map +1 -0
- package/dist/schema-Bu2noOZ4.mjs +27 -0
- package/dist/schema-Bu2noOZ4.mjs.map +1 -0
- package/dist/schema-CwFvuCnA.mjs +97 -0
- package/dist/schema-CwFvuCnA.mjs.map +1 -0
- package/dist/schema-Wz-1-ro9.d.mts +37 -0
- package/dist/schema-Wz-1-ro9.d.mts.map +1 -0
- package/dist/schemas-CxQtxIga.mjs +62 -0
- package/dist/schemas-CxQtxIga.mjs.map +1 -0
- package/dist/schemas-DBOhxgW7.d.mts +32 -0
- package/dist/schemas-DBOhxgW7.d.mts.map +1 -0
- package/dist/schemas-Dp_OCqBt.d.mts +63 -0
- package/dist/schemas-Dp_OCqBt.d.mts.map +1 -0
- package/dist/schemas-SwCsnT0z.mjs +83 -0
- package/dist/schemas-SwCsnT0z.mjs.map +1 -0
- package/dist/sdk-errors.d.mts +2 -0
- package/dist/sdk-errors.mjs +3 -0
- package/dist/sdk-experimental.d.mts +59 -0
- package/dist/sdk-experimental.d.mts.map +1 -0
- package/dist/sdk-experimental.mjs +193 -0
- package/dist/sdk-experimental.mjs.map +1 -0
- package/dist/sdk-stop-conditions.d.mts +3 -0
- package/dist/sdk-stop-conditions.mjs +3 -0
- package/dist/sdk.d.mts +15 -0
- package/dist/sdk.d.mts.map +1 -0
- package/dist/sdk.mjs +50 -0
- package/dist/sdk.mjs.map +1 -0
- package/dist/security-guardrails.d.mts +3 -0
- package/dist/security-guardrails.mjs +3 -0
- package/dist/security-injection.d.mts +2 -0
- package/dist/security-injection.mjs +3 -0
- package/dist/security.d.mts +12 -0
- package/dist/security.d.mts.map +1 -0
- package/dist/security.mjs +18 -0
- package/dist/security.mjs.map +1 -0
- package/dist/server.d.mts +420 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +2225 -0
- package/dist/server.mjs.map +1 -0
- package/dist/shared.d.mts +2 -0
- package/dist/shared.mjs +3 -0
- package/dist/streaming-control.d.mts +2 -0
- package/dist/streaming-control.mjs +4 -0
- package/dist/streaming-core.d.mts +4 -0
- package/dist/streaming-core.mjs +3 -0
- package/dist/streaming-infra-resilience.d.mts +120 -0
- package/dist/streaming-infra-resilience.d.mts.map +1 -0
- package/dist/streaming-infra-resilience.mjs +358 -0
- package/dist/streaming-infra-resilience.mjs.map +1 -0
- package/dist/streaming-infra-transport.d.mts +57 -0
- package/dist/streaming-infra-transport.d.mts.map +1 -0
- package/dist/streaming-infra-transport.mjs +488 -0
- package/dist/streaming-infra-transport.mjs.map +1 -0
- package/dist/streaming-infra.d.mts +5 -0
- package/dist/streaming-infra.mjs +5 -0
- package/dist/streaming.d.mts +17 -0
- package/dist/streaming.d.mts.map +1 -0
- package/dist/streaming.mjs +71 -0
- package/dist/streaming.mjs.map +1 -0
- package/dist/telemetry-2eKMojIb.mjs +1046 -0
- package/dist/telemetry-2eKMojIb.mjs.map +1 -0
- package/dist/telemetry-C2t03dwD.d.mts +59 -0
- package/dist/telemetry-C2t03dwD.d.mts.map +1 -0
- package/dist/tool-Btbththq.d.mts +253 -0
- package/dist/tool-Btbththq.d.mts.map +1 -0
- package/dist/tool-JSf8JXZ8.mjs +1150 -0
- package/dist/tool-JSf8JXZ8.mjs.map +1 -0
- package/dist/tool-safety-CZO8a4D4.d.mts +60 -0
- package/dist/tool-safety-CZO8a4D4.d.mts.map +1 -0
- package/dist/tool-safety-DXtYDXod.mjs +319 -0
- package/dist/tool-safety-DXtYDXod.mjs.map +1 -0
- package/dist/tools-BuS2Uv0q.mjs +1708 -0
- package/dist/tools-BuS2Uv0q.mjs.map +1 -0
- package/dist/tools-approval.d.mts +99 -0
- package/dist/tools-approval.d.mts.map +1 -0
- package/dist/tools-approval.mjs +395 -0
- package/dist/tools-approval.mjs.map +1 -0
- package/dist/tools-compliance.d.mts +67 -0
- package/dist/tools-compliance.d.mts.map +1 -0
- package/dist/tools-compliance.mjs +330 -0
- package/dist/tools-compliance.mjs.map +1 -0
- package/dist/tools-computer.d.mts +25 -0
- package/dist/tools-computer.d.mts.map +1 -0
- package/dist/tools-computer.mjs +64 -0
- package/dist/tools-computer.mjs.map +1 -0
- package/dist/tools-core.d.mts +3 -0
- package/dist/tools-core.mjs +3 -0
- package/dist/tools-mcp.d.mts +3 -0
- package/dist/tools-mcp.mjs +5 -0
- package/dist/tools-superpowers.d.mts +2 -0
- package/dist/tools-superpowers.mjs +3 -0
- package/dist/tools.d.mts +401 -0
- package/dist/tools.d.mts.map +1 -0
- package/dist/tools.mjs +1921 -0
- package/dist/tools.mjs.map +1 -0
- package/dist/transport-selector-D-Ib05X1.mjs +1936 -0
- package/dist/transport-selector-D-Ib05X1.mjs.map +1 -0
- package/dist/triage-agent-BEsXg5sw.d.mts +63 -0
- package/dist/triage-agent-BEsXg5sw.d.mts.map +1 -0
- package/dist/triage-agent-CBsfX-HW.mjs +167 -0
- package/dist/triage-agent-CBsfX-HW.mjs.map +1 -0
- package/dist/types-BPnq3GQo.d.mts +23 -0
- package/dist/types-BPnq3GQo.d.mts.map +1 -0
- package/dist/types-BjWgimpY.d.mts +16 -0
- package/dist/types-BjWgimpY.d.mts.map +1 -0
- package/dist/types-BxD-5btB.d.mts +41 -0
- package/dist/types-BxD-5btB.d.mts.map +1 -0
- package/dist/types-By-r93bE.d.mts +36 -0
- package/dist/types-By-r93bE.d.mts.map +1 -0
- package/dist/types-CLBWFRZN.d.mts +69 -0
- package/dist/types-CLBWFRZN.d.mts.map +1 -0
- package/dist/types-CQ0HFd0u.d.mts +62 -0
- package/dist/types-CQ0HFd0u.d.mts.map +1 -0
- package/dist/types-D3zJb59_.d.mts +47 -0
- package/dist/types-D3zJb59_.d.mts.map +1 -0
- package/dist/types-DJnugQX0.d.mts +80 -0
- package/dist/types-DJnugQX0.d.mts.map +1 -0
- package/dist/types-DbUfMCnT.d.mts +70 -0
- package/dist/types-DbUfMCnT.d.mts.map +1 -0
- package/dist/upstash-adapter-D96Caq2O.mjs +22 -0
- package/dist/upstash-adapter-D96Caq2O.mjs.map +1 -0
- package/dist/upstash-adapter-DD4433dx.d.mts +8 -0
- package/dist/upstash-adapter-DD4433dx.d.mts.map +1 -0
- package/dist/utils-BlYhcD6M.mjs +319 -0
- package/dist/utils-BlYhcD6M.mjs.map +1 -0
- package/dist/utils-DpJGOb3y.d.mts +120 -0
- package/dist/utils-DpJGOb3y.d.mts.map +1 -0
- package/dist/vercel-blob-adapter-CkOXLT2D.mjs +25 -0
- package/dist/vercel-blob-adapter-CkOXLT2D.mjs.map +1 -0
- package/dist/xai-CbV_dCnP.mjs +1600 -0
- package/dist/xai-CbV_dCnP.mjs.map +1 -0
- package/package.json +479 -0
- package/src/agents/base/factory.ts +382 -0
- package/src/agents/base/index.ts +8 -0
- package/src/agents/base/schemas.ts +117 -0
- package/src/agents/base/types.ts +192 -0
- package/src/agents/control-flow/index.ts +683 -0
- package/src/agents/coordinator/coordinator-agent.ts +381 -0
- package/src/agents/coordinator/index.ts +6 -0
- package/src/agents/default-agent.ts +211 -0
- package/src/agents/evaluator-optimizer/README.md +612 -0
- package/src/agents/evaluator-optimizer/evaluator-optimizer.example.ts +437 -0
- package/src/agents/evaluator-optimizer/evaluator.ts +282 -0
- package/src/agents/evaluator-optimizer/index.test.ts +416 -0
- package/src/agents/evaluator-optimizer/index.ts +519 -0
- package/src/agents/evaluator-optimizer/optimizer.ts +322 -0
- package/src/agents/evaluator-optimizer/schema.ts +302 -0
- package/src/agents/evaluator-optimizer/utils.ts +42 -0
- package/src/agents/experimental/index.ts +1095 -0
- package/src/agents/experimental/types.ts +212 -0
- package/src/agents/fallback/index.ts +18 -0
- package/src/agents/fallback/recovery/circuit-breaker.ts +166 -0
- package/src/agents/fallback/strategies/model-fallback.ts +192 -0
- package/src/agents/fallback/types.ts +87 -0
- package/src/agents/governance-agent.ts +446 -0
- package/src/agents/index.ts +79 -0
- package/src/agents/multi/coordination/index.ts +6 -0
- package/src/agents/multi/coordination/message-bus.ts +144 -0
- package/src/agents/multi/index.ts +6 -0
- package/src/agents/multi/state/index.ts +162 -0
- package/src/agents/multi/supervisor/index.ts +7 -0
- package/src/agents/multi/supervisor/supervisor.ts +254 -0
- package/src/agents/multi/swarm/aggregation.ts +466 -0
- package/src/agents/multi/swarm/communication.ts +388 -0
- package/src/agents/multi/swarm/coordination.ts +380 -0
- package/src/agents/multi/swarm/index.ts +73 -0
- package/src/agents/multi/swarm/swarm-executor.ts +479 -0
- package/src/agents/multi/types.ts +181 -0
- package/src/agents/observability/index.ts +914 -0
- package/src/agents/orchestrator.ts +218 -0
- package/src/agents/patterns/README.md +512 -0
- package/src/agents/patterns/evaluator-optimizer-pattern.example.ts +455 -0
- package/src/agents/patterns/evaluator-optimizer-pattern.ts +653 -0
- package/src/agents/patterns/index.ts +26 -0
- package/src/agents/persistence/index.ts +726 -0
- package/src/agents/tools/index.ts +291 -0
- package/src/agents/tools/mcp.ts +188 -0
- package/src/agents/triage/index.ts +6 -0
- package/src/agents/triage/triage-agent.ts +280 -0
- package/src/agents/workflows/index.ts +6 -0
- package/src/agents/workflows/interfaces.ts +36 -0
- package/src/agents/workflows/schema.ts +20 -0
- package/src/caching/adapters/index.ts +7 -0
- package/src/caching/adapters/memory.ts +77 -0
- package/src/caching/adapters/redis.ts +60 -0
- package/src/caching/index.ts +17 -0
- package/src/caching/middleware.ts +452 -0
- package/src/caching/strategies/index.ts +1008 -0
- package/src/caching/types.ts +47 -0
- package/src/catalog.ts +921 -0
- package/src/client/chat-usage.ts +53 -0
- package/src/client/hooks.ts +343 -0
- package/src/client/index.ts +36 -0
- package/src/client/message-utils.ts +29 -0
- package/src/client/use-generative-ui.ts +174 -0
- package/src/client/utils.ts +66 -0
- package/src/generative-ui/catalog.ts +653 -0
- package/src/generative-ui/index.ts +82 -0
- package/src/generative-ui/registry.ts +273 -0
- package/src/generative-ui/stream.ts +324 -0
- package/src/generative-ui/types.ts +376 -0
- package/src/governance/audit/audit-logger.ts +239 -0
- package/src/governance/audit/audit-schema.ts +82 -0
- package/src/governance/audit/index.ts +6 -0
- package/src/governance/compliance/abac/policy-engine.ts +175 -0
- package/src/governance/compliance/abac/types.ts +40 -0
- package/src/governance/compliance/approval/compliance-approval-queue.ts +217 -0
- package/src/governance/compliance/index.ts +16 -0
- package/src/governance/compliance/schemas.ts +68 -0
- package/src/governance/compliance/types.ts +143 -0
- package/src/governance/compliance/validators/phi-detector.ts +145 -0
- package/src/governance/compliance/validators/redaction-utils.ts +176 -0
- package/src/governance/compliance/validators/safe-harbor.ts +135 -0
- package/src/governance/entitlements/index.ts +585 -0
- package/src/governance/entitlements/middleware.ts +651 -0
- package/src/governance/entitlements/rate-limiter.ts +711 -0
- package/src/governance/index.ts +32 -0
- package/src/governance/policies/guardrails.ts +1121 -0
- package/src/governance/policies/index.ts +42 -0
- package/src/governance/policies/loop-controls.ts +136 -0
- package/src/governance/policies/telemetry.ts +63 -0
- package/src/governance/tenancy/index.ts +30 -0
- package/src/governance/tenancy/isolation/context.ts +92 -0
- package/src/governance/tenancy/isolation/index.ts +13 -0
- package/src/governance/tenancy/quotas/index.ts +11 -0
- package/src/governance/tenancy/quotas/quota-manager.ts +180 -0
- package/src/governance/tenancy/types.ts +66 -0
- package/src/governance/types.ts +16 -0
- package/src/governance/versioning/index.ts +573 -0
- package/src/grounding/attribution/index.ts +424 -0
- package/src/grounding/citation/citation-generator.ts +174 -0
- package/src/grounding/citation/index.ts +12 -0
- package/src/grounding/context/index.ts +32 -0
- package/src/grounding/context/safe-context.ts +116 -0
- package/src/grounding/context/types.ts +62 -0
- package/src/grounding/context-engineering/error-handling.ts +359 -0
- package/src/grounding/context-engineering/index.ts +23 -0
- package/src/grounding/context-engineering/memory.ts +559 -0
- package/src/grounding/context-engineering/tool-masking.ts +338 -0
- package/src/grounding/embed/index.ts +704 -0
- package/src/grounding/embed/reranking.ts +604 -0
- package/src/grounding/hallucination/index.ts +223 -0
- package/src/grounding/index.ts +82 -0
- package/src/grounding/proof-map/applyPatch.ts +172 -0
- package/src/grounding/proof-map/index.ts +41 -0
- package/src/grounding/proof-map/loop.ts +275 -0
- package/src/grounding/proof-map/schema.ts +217 -0
- package/src/grounding/rag/__tests__/pipeline.test.ts +274 -0
- package/src/grounding/rag/__tests__/tool.test.ts +202 -0
- package/src/grounding/rag/__tests__/trace.test.ts +229 -0
- package/src/grounding/rag/circuit-breaker.ts +152 -0
- package/src/grounding/rag/index.ts +64 -0
- package/src/grounding/rag/pipeline.ts +602 -0
- package/src/grounding/rag/tool.ts +281 -0
- package/src/grounding/rag/trace.ts +503 -0
- package/src/grounding/rag/types.ts +284 -0
- package/src/grounding/retrieval/in-memory-store.ts +107 -0
- package/src/grounding/sources/index.ts +943 -0
- package/src/grounding/tests/applyPatch.test.ts +194 -0
- package/src/grounding/tests/loop.test.ts +141 -0
- package/src/grounding/tests/schema.test.ts +160 -0
- package/src/grounding/types.ts +100 -0
- package/src/grounding/verification/index.ts +419 -0
- package/src/hitl/active-learning/index.ts +332 -0
- package/src/hitl/annotation/index.ts +362 -0
- package/src/hitl/approval/approval-queue.ts +132 -0
- package/src/hitl/approval/index.ts +5 -0
- package/src/hitl/feedback/index.ts +284 -0
- package/src/hitl/index.ts +69 -0
- package/src/hitl/review/index.ts +6 -0
- package/src/hitl/review/review-trigger.ts +162 -0
- package/src/hitl/types.ts +126 -0
- package/src/index.ts +125 -0
- package/src/integrations/blob-storage/index.ts +7 -0
- package/src/integrations/blob-storage/types.ts +28 -0
- package/src/integrations/blob-storage/vercel-blob-adapter.ts +35 -0
- package/src/integrations/index.ts +15 -0
- package/src/integrations/notifications/index.ts +7 -0
- package/src/integrations/notifications/log-adapter.ts +30 -0
- package/src/integrations/notifications/types.ts +27 -0
- package/src/integrations/rate-limit/index.ts +7 -0
- package/src/integrations/rate-limit/types.ts +26 -0
- package/src/integrations/rate-limit/upstash-adapter.ts +45 -0
- package/src/integrations/redis/index.ts +7 -0
- package/src/integrations/redis/types.ts +67 -0
- package/src/integrations/redis/upstash-adapter.ts +18 -0
- package/src/integrations/stream/index.ts +7 -0
- package/src/integrations/stream/resumable-adapter.ts +20 -0
- package/src/integrations/stream/types.ts +21 -0
- package/src/internal/__tests__/hallucination.test.ts +162 -0
- package/src/internal/__tests__/models.test.ts +104 -0
- package/src/internal/__tests__/sdk-errors.test.ts +201 -0
- package/src/internal/__tests__/stop-conditions.test.ts +210 -0
- package/src/internal/shared/ai-types.ts +942 -0
- package/src/internal/testing/evaluators.ts +575 -0
- package/src/internal/testing/index.ts +960 -0
- package/src/internal/ui/data-parts.ts +511 -0
- package/src/internal/ui/type-guards.ts +344 -0
- package/src/internal/ui-factories/__tests__/ui-factories.test.ts +548 -0
- package/src/internal/ui-factories/artifact-factory.ts +667 -0
- package/src/internal/ui-factories/index.ts +82 -0
- package/src/internal/ui-factories/shimmer-manager.ts +220 -0
- package/src/internal/ui-factories/status-helpers.ts +149 -0
- package/src/internal/ui-factories/tool-renderer.ts +167 -0
- package/src/internal/ui-factories/types.ts +235 -0
- package/src/models/capabilities.ts +88 -0
- package/src/models/index.ts +16 -0
- package/src/models/provider-factory.ts +229 -0
- package/src/models/providers/anthropic.ts +539 -0
- package/src/models/providers/google.ts +354 -0
- package/src/models/providers/index.ts +21 -0
- package/src/models/providers/openai.ts +346 -0
- package/src/models/providers/perplexity.ts +276 -0
- package/src/models/providers/shared.ts +90 -0
- package/src/models/providers/xai.ts +269 -0
- package/src/models/registry.ts +208 -0
- package/src/models/routing/index.ts +45 -0
- package/src/models/routing/intent-router.ts +143 -0
- package/src/models/routing/model-router.ts +300 -0
- package/src/models/routing/types.ts +106 -0
- package/src/models/types.ts +23 -0
- package/src/observability/analytics/index.ts +593 -0
- package/src/observability/cost/index.ts +16 -0
- package/src/observability/cost/tracking/budget-guard.ts +110 -0
- package/src/observability/cost/tracking/usage-tracker.ts +120 -0
- package/src/observability/cost/types.ts +85 -0
- package/src/observability/index.ts +17 -0
- package/src/observability/telemetry/index.ts +508 -0
- package/src/observability/tracing/index.ts +30 -0
- package/src/observability/tracing/otel/ai-instrumentation.ts +193 -0
- package/src/observability/tracing/otel/exporters/console.ts +58 -0
- package/src/observability/tracing/otel/exporters/index.ts +6 -0
- package/src/observability/tracing/provenance.ts +769 -0
- package/src/observability/tracing/types.ts +92 -0
- package/src/output/__tests__/output.test.ts +737 -0
- package/src/output/element-stream.ts +678 -0
- package/src/output/errors.ts +108 -0
- package/src/output/factories.ts +392 -0
- package/src/output/index.ts +98 -0
- package/src/output/multimodal/EXPORTS.md +306 -0
- package/src/output/multimodal/IMPLEMENTATION_SUMMARY.md +421 -0
- package/src/output/multimodal/README.md +349 -0
- package/src/output/multimodal/SETUP_GUIDE.md +472 -0
- package/src/output/multimodal/audio.ts +650 -0
- package/src/output/multimodal/image.ts +22 -0
- package/src/output/multimodal/index.ts +32 -0
- package/src/output/multimodal/providers.example.ts +375 -0
- package/src/output/validator.ts +495 -0
- package/src/pipelines/adapters/trace-storage-blob.ts +458 -0
- package/src/pipelines/adapters/trace-storage-memory.ts +319 -0
- package/src/pipelines/defaults.ts +109 -0
- package/src/pipelines/index.ts +24 -0
- package/src/pipelines/message-transforms.ts +107 -0
- package/src/pipelines/multi-step-wrapper.ts +433 -0
- package/src/pipelines/pipeline-presets.ts +339 -0
- package/src/pipelines/step-executor.ts +257 -0
- package/src/pipelines/storage-factory.ts +85 -0
- package/src/pipelines/trace-storage-interface.ts +216 -0
- package/src/pipelines/types.ts +255 -0
- package/src/pipelines/validation.ts +323 -0
- package/src/prompts/index.ts +271 -0
- package/src/prompts/model-variants.ts +410 -0
- package/src/prompts/templates.ts +327 -0
- package/src/sdk/errors/base.ts +296 -0
- package/src/sdk/errors/index.ts +31 -0
- package/src/sdk/errors/utils.ts +148 -0
- package/src/sdk/experimental/index.ts +286 -0
- package/src/sdk/index.ts +25 -0
- package/src/sdk/middleware/ai-middleware.ts +95 -0
- package/src/sdk/middleware/cache.ts +154 -0
- package/src/sdk/middleware/circuit-breaker.ts +388 -0
- package/src/sdk/middleware/compose.ts +81 -0
- package/src/sdk/middleware/deduplication.ts +307 -0
- package/src/sdk/middleware/index.ts +660 -0
- package/src/sdk/middleware/model-middleware.ts +200 -0
- package/src/sdk/stop-conditions/conditions.ts +209 -0
- package/src/sdk/stop-conditions/index.ts +35 -0
- package/src/sdk/stop-conditions/types.ts +59 -0
- package/src/security/guardrails/index.ts +6 -0
- package/src/security/guardrails/middleware.ts +465 -0
- package/src/security/guardrails/pii-filter.ts +396 -0
- package/src/security/index.ts +33 -0
- package/src/security/injection/index.ts +5 -0
- package/src/security/injection/prompt-injection.ts +64 -0
- package/src/security/types.ts +85 -0
- package/src/server/cache/crypto.ts +47 -0
- package/src/server/cache/performance.ts +79 -0
- package/src/server/error-handler.ts +93 -0
- package/src/server/errors.ts +73 -0
- package/src/server/helpers.ts +944 -0
- package/src/server/http.ts +156 -0
- package/src/server/index.ts +12 -0
- package/src/server/messages/__tests__/messages.test.ts +720 -0
- package/src/server/messages/converter.ts +245 -0
- package/src/server/messages/data-parts.ts +338 -0
- package/src/server/messages/extraction.ts +328 -0
- package/src/server/messages/index.ts +126 -0
- package/src/server/messages/types.ts +355 -0
- package/src/server/messages/window.ts +450 -0
- package/src/server/rate-limit/env.ts +8 -0
- package/src/server/rate-limit/rate-limit.ts +165 -0
- package/src/server/routes/HEALTH_CHECK.md +502 -0
- package/src/server/routes/IMPLEMENTATION_SUMMARY.md +432 -0
- package/src/server/routes/QUICK_START.md +327 -0
- package/src/server/routes/README.md +357 -0
- package/src/server/routes/__tests__/routes.test.ts +628 -0
- package/src/server/routes/agent-route.ts +224 -0
- package/src/server/routes/agent-routes.ts +191 -0
- package/src/server/routes/embed-config.ts +181 -0
- package/src/server/routes/health-check.example.ts +507 -0
- package/src/server/routes/health-check.test.ts +533 -0
- package/src/server/routes/health-check.ts +639 -0
- package/src/server/routes/health-check.types.ts +217 -0
- package/src/server/routes/index.ts +32 -0
- package/src/server/routes/types.ts +274 -0
- package/src/shared/__tests__/schemas.test.ts +317 -0
- package/src/shared/ai-runtime.ts +139 -0
- package/src/shared/ai-types.ts +133 -0
- package/src/shared/index.ts +30 -0
- package/src/shared/sdk-runtime.ts +198 -0
- package/src/shared/sdk-types.ts +301 -0
- package/src/streaming/control/__tests__/streaming-control.test.ts +708 -0
- package/src/streaming/control/budget-guard.ts +264 -0
- package/src/streaming/control/controller.ts +255 -0
- package/src/streaming/control/index.ts +105 -0
- package/src/streaming/control/smoothing.ts +201 -0
- package/src/streaming/control/step-limit.ts +215 -0
- package/src/streaming/control/types.ts +234 -0
- package/src/streaming/core/auto-resume.ts +276 -0
- package/src/streaming/core/index.ts +85 -0
- package/src/streaming/core/multi-step.ts +471 -0
- package/src/streaming/core/protocol.ts +194 -0
- package/src/streaming/core/types.ts +182 -0
- package/src/streaming/index.ts +97 -0
- package/src/streaming/infra/backpressure/buffer.ts +210 -0
- package/src/streaming/infra/backpressure/index.ts +6 -0
- package/src/streaming/infra/index.ts +75 -0
- package/src/streaming/infra/multiplexing/index.ts +311 -0
- package/src/streaming/infra/resilience/index.ts +684 -0
- package/src/streaming/infra/transform/index.ts +15 -0
- package/src/streaming/infra/transform/stream-transforms.ts +166 -0
- package/src/streaming/infra/transport/index.ts +774 -0
- package/src/streaming/infra/types.ts +118 -0
- package/src/streaming/infra-extra/types.ts +118 -0
- package/src/tools/advanced/caching.ts +299 -0
- package/src/tools/advanced/generator.ts +267 -0
- package/src/tools/advanced/hitl.ts +251 -0
- package/src/tools/advanced/index.ts +9 -0
- package/src/tools/advanced/llm-tool.ts +208 -0
- package/src/tools/approval/FILES.md +449 -0
- package/src/tools/approval/IMPLEMENTATION_SUMMARY.md +567 -0
- package/src/tools/approval/QUICK_START.md +362 -0
- package/src/tools/approval/README.md +514 -0
- package/src/tools/approval/advanced-approval-queue.ts +7 -0
- package/src/tools/approval/approval.example.ts +571 -0
- package/src/tools/approval/in-memory-queue.ts +405 -0
- package/src/tools/approval/index.ts +737 -0
- package/src/tools/approval/middleware.ts +590 -0
- package/src/tools/approval/queue-factory.ts +162 -0
- package/src/tools/approval/redis-queue.ts +327 -0
- package/src/tools/approval/testing.ts +493 -0
- package/src/tools/approval/types.ts +221 -0
- package/src/tools/approval/with-approval.ts +366 -0
- package/src/tools/artifacts/artifact-tools.ts +273 -0
- package/src/tools/artifacts/index.ts +6 -0
- package/src/tools/compliance/compliance-wrapper.ts +789 -0
- package/src/tools/compliance/create-compliant-stream.ts +226 -0
- package/src/tools/compliance/index.ts +8 -0
- package/src/tools/compliance/phi-redaction.ts +406 -0
- package/src/tools/compliance/tool-wrapper.ts +306 -0
- package/src/tools/computer/index.ts +99 -0
- package/src/tools/computer/types.ts +41 -0
- package/src/tools/core/abort.ts +202 -0
- package/src/tools/core/factory.ts +197 -0
- package/src/tools/core/index.ts +8 -0
- package/src/tools/core/tool-safety.ts +112 -0
- package/src/tools/generic/index.ts +9 -0
- package/src/tools/generic/json-schema-tool.ts +301 -0
- package/src/tools/generic/tiptap-context.ts +619 -0
- package/src/tools/generic/web-search-tool.ts +82 -0
- package/src/tools/generic/web-search.ts +142 -0
- package/src/tools/index.ts +36 -0
- package/src/tools/mcp/ai-sdk-error-integration.ts +401 -0
- package/src/tools/mcp/client.ts +988 -0
- package/src/tools/mcp/connection-manager.ts +380 -0
- package/src/tools/mcp/connection-pool.ts +408 -0
- package/src/tools/mcp/edge-runtime.ts +318 -0
- package/src/tools/mcp/environment.ts +310 -0
- package/src/tools/mcp/index.ts +20 -0
- package/src/tools/mcp/next-pattern.ts +401 -0
- package/src/tools/mcp/stream-lifecycle-integration.ts +617 -0
- package/src/tools/mcp/tool-cache.ts +359 -0
- package/src/tools/mcp/transport-selector.ts +492 -0
- package/src/tools/mcp/transports.ts +99 -0
- package/src/tools/simple-factory.ts +55 -0
- package/src/tools/superpowers/index.ts +122 -0
- package/src/tools/superpowers/prompts/index.ts +533 -0
- package/src/tools/superpowers/schemas/index.ts +701 -0
- package/src/tools/superpowers/tools/index.ts +721 -0
- package/src/tools/validation-wrapper.ts +97 -0
package/dist/server.mjs
ADDED
|
@@ -0,0 +1,2225 @@
|
|
|
1
|
+
import { Nt as streamText, Z as generateId, p as UI_MESSAGE_STREAM_HEADERS, w as createAgentUIStreamResponse } from "./ai-runtime-CDzQztTt.mjs";
|
|
2
|
+
import { i as normalizeError, n as formatErrorMessage, r as getErrorCode, t as formatConnectionError } from "./errors-Dtn-UeRi.mjs";
|
|
3
|
+
import { r as verifySignature, t as generateSignature } from "./crypto-8ABhc3TD.mjs";
|
|
4
|
+
import { i as wrapWithCompliance, r as preprocessMessages } from "./compliance-wrapper-CrOMHhHN.mjs";
|
|
5
|
+
import { t as createResumableStream } from "./resumable-adapter-CO1HtsgJ.mjs";
|
|
6
|
+
import { t as createLogNotifier } from "./log-adapter-PPe_2Pwv.mjs";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { logError, logInfo, logWarn } from "@od-oneapp/shared/logs";
|
|
9
|
+
import { createAIConversationOrm, createAIMessageOrm, findAIConversationOrm, updateAIConversationOrm } from "@od-oneapp/db-prisma/orm";
|
|
10
|
+
import { AIMessageRole, AISessionType } from "@od-oneapp/db-prisma/types";
|
|
11
|
+
|
|
12
|
+
//#region src/server/routes/health-check.ts
|
|
13
|
+
/**
|
|
14
|
+
* Check database connectivity
|
|
15
|
+
*/
|
|
16
|
+
async function checkDatabase(config, timeoutMs) {
|
|
17
|
+
if (config.enableDatabase === false) return {
|
|
18
|
+
connected: true,
|
|
19
|
+
status: "up"
|
|
20
|
+
};
|
|
21
|
+
const databaseUrl = config.databaseUrl || process.env.DATABASE_URL || process.env.POSTGRES_URL;
|
|
22
|
+
if (!databaseUrl) return {
|
|
23
|
+
connected: false,
|
|
24
|
+
status: "down",
|
|
25
|
+
error: "DATABASE_URL not configured"
|
|
26
|
+
};
|
|
27
|
+
try {
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
30
|
+
const startTime = Date.now();
|
|
31
|
+
try {
|
|
32
|
+
const postgresModule = await import("postgres");
|
|
33
|
+
const postgresDefault = postgresModule && typeof postgresModule === "object" && "default" in postgresModule ? postgresModule.default : void 0;
|
|
34
|
+
if (typeof postgresDefault !== "function") throw new Error("postgres module is unavailable");
|
|
35
|
+
const client = postgresDefault(databaseUrl, {
|
|
36
|
+
prepare: false,
|
|
37
|
+
max: 1,
|
|
38
|
+
idle_timeout: 3
|
|
39
|
+
});
|
|
40
|
+
await Promise.race([client`SELECT 1`, new Promise((resolve, reject) => {
|
|
41
|
+
setTimeout(() => reject(/* @__PURE__ */ new Error("Timeout")), timeoutMs);
|
|
42
|
+
})]);
|
|
43
|
+
await client.end();
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
46
|
+
throw new Error(`Database connection failed: ${errorMessage}`);
|
|
47
|
+
}
|
|
48
|
+
const latency = Date.now() - startTime;
|
|
49
|
+
clearTimeout(timeout);
|
|
50
|
+
return {
|
|
51
|
+
connected: true,
|
|
52
|
+
status: "up",
|
|
53
|
+
latency
|
|
54
|
+
};
|
|
55
|
+
} catch (error) {
|
|
56
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
57
|
+
return {
|
|
58
|
+
connected: false,
|
|
59
|
+
status: "down",
|
|
60
|
+
error: config.includeSensitiveDetails ? errorMessage : "Connection failed"
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Check Redis connectivity
|
|
66
|
+
*/
|
|
67
|
+
async function checkRedis(config, timeoutMs) {
|
|
68
|
+
if (config.enableRedis === false) return {
|
|
69
|
+
connected: true,
|
|
70
|
+
status: "up"
|
|
71
|
+
};
|
|
72
|
+
const redisUrl = config.redisUrl || process.env.REDIS_URL;
|
|
73
|
+
if (!redisUrl) return {
|
|
74
|
+
connected: true,
|
|
75
|
+
status: "up"
|
|
76
|
+
};
|
|
77
|
+
try {
|
|
78
|
+
const controller = new AbortController();
|
|
79
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
80
|
+
const startTime = Date.now();
|
|
81
|
+
try {
|
|
82
|
+
const redisModule = await import("redis");
|
|
83
|
+
const createClient = redisModule && typeof redisModule === "object" && "createClient" in redisModule ? redisModule.createClient : void 0;
|
|
84
|
+
if (typeof createClient !== "function") throw new Error("redis module is unavailable");
|
|
85
|
+
const redisClient = createClient({
|
|
86
|
+
url: redisUrl,
|
|
87
|
+
socket: { reconnectStrategy: () => false }
|
|
88
|
+
});
|
|
89
|
+
await Promise.race([redisClient.connect().then(() => redisClient.ping()), new Promise((resolve, reject) => {
|
|
90
|
+
setTimeout(() => reject(/* @__PURE__ */ new Error("Timeout")), timeoutMs);
|
|
91
|
+
})]);
|
|
92
|
+
await redisClient.close();
|
|
93
|
+
} catch {
|
|
94
|
+
const response = await Promise.race([fetch(redisUrl, { signal: controller.signal }), new Promise((resolve, reject) => {
|
|
95
|
+
setTimeout(() => reject(/* @__PURE__ */ new Error("Timeout")), timeoutMs);
|
|
96
|
+
})]);
|
|
97
|
+
if (!response || response.status >= 400) throw new Error("Redis connection failed");
|
|
98
|
+
}
|
|
99
|
+
const latency = Date.now() - startTime;
|
|
100
|
+
clearTimeout(timeout);
|
|
101
|
+
return {
|
|
102
|
+
connected: true,
|
|
103
|
+
status: "up",
|
|
104
|
+
latency
|
|
105
|
+
};
|
|
106
|
+
} catch (error) {
|
|
107
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
108
|
+
return {
|
|
109
|
+
connected: false,
|
|
110
|
+
status: "down",
|
|
111
|
+
error: config.includeSensitiveDetails ? errorMessage : "Connection failed"
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Check AI provider availability
|
|
117
|
+
*/
|
|
118
|
+
async function checkAIProvider(config, timeoutMs) {
|
|
119
|
+
if (config.enableAIProvider === false) return {
|
|
120
|
+
available: true,
|
|
121
|
+
status: "up"
|
|
122
|
+
};
|
|
123
|
+
try {
|
|
124
|
+
const startTime = Date.now();
|
|
125
|
+
const providers = {
|
|
126
|
+
openai: true,
|
|
127
|
+
anthropic: true,
|
|
128
|
+
gemini: true
|
|
129
|
+
};
|
|
130
|
+
if (config.enableAIProviderTest) try {
|
|
131
|
+
const { generateText } = await import("ai");
|
|
132
|
+
const { openai } = await import("@ai-sdk/openai");
|
|
133
|
+
const controller = new AbortController();
|
|
134
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
135
|
+
try {
|
|
136
|
+
await Promise.race([generateText({
|
|
137
|
+
model: openai("gpt-4o-mini"),
|
|
138
|
+
prompt: "health",
|
|
139
|
+
maxOutputTokens: 1,
|
|
140
|
+
abortSignal: controller.signal
|
|
141
|
+
}).catch(() => {
|
|
142
|
+
return null;
|
|
143
|
+
}), new Promise((resolve, reject) => {
|
|
144
|
+
setTimeout(() => reject(/* @__PURE__ */ new Error("Timeout")), timeoutMs);
|
|
145
|
+
})]);
|
|
146
|
+
} finally {
|
|
147
|
+
clearTimeout(timeout);
|
|
148
|
+
}
|
|
149
|
+
} catch {}
|
|
150
|
+
return {
|
|
151
|
+
available: true,
|
|
152
|
+
status: "up",
|
|
153
|
+
providers,
|
|
154
|
+
latency: Date.now() - startTime
|
|
155
|
+
};
|
|
156
|
+
} catch (error) {
|
|
157
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
158
|
+
return {
|
|
159
|
+
available: false,
|
|
160
|
+
status: "down",
|
|
161
|
+
error: config.includeSensitiveDetails ? errorMessage : "Provider check failed"
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Check system memory usage
|
|
167
|
+
*/
|
|
168
|
+
function checkMemory(config) {
|
|
169
|
+
if (config.enableMemory === false) return {
|
|
170
|
+
heapUsed: 0,
|
|
171
|
+
heapTotal: 0,
|
|
172
|
+
external: 0,
|
|
173
|
+
rss: 0,
|
|
174
|
+
heapUsedPercent: 0,
|
|
175
|
+
status: "ok"
|
|
176
|
+
};
|
|
177
|
+
const memUsage = process.memoryUsage();
|
|
178
|
+
const heapUsedPercent = memUsage.heapUsed / memUsage.heapTotal * 100;
|
|
179
|
+
const warningThreshold = config.memoryWarningThreshold ?? 80;
|
|
180
|
+
const criticalThreshold = config.memoryCriticalThreshold ?? 90;
|
|
181
|
+
let status = "ok";
|
|
182
|
+
if (heapUsedPercent >= criticalThreshold) status = "critical";
|
|
183
|
+
else if (heapUsedPercent >= warningThreshold) status = "warning";
|
|
184
|
+
return {
|
|
185
|
+
heapUsed: Math.round(memUsage.heapUsed),
|
|
186
|
+
heapTotal: Math.round(memUsage.heapTotal),
|
|
187
|
+
external: Math.round(memUsage.external),
|
|
188
|
+
rss: Math.round(memUsage.rss),
|
|
189
|
+
heapUsedPercent: Math.round(heapUsedPercent * 100) / 100,
|
|
190
|
+
status
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Determine overall service status based on component health
|
|
195
|
+
*/
|
|
196
|
+
function determineStatus(database, redis, aiProvider, memory) {
|
|
197
|
+
if (database && !database.connected) return "unhealthy";
|
|
198
|
+
if (redis && !redis.connected || aiProvider && !aiProvider.available) return "degraded";
|
|
199
|
+
if (memory?.status === "critical") return "degraded";
|
|
200
|
+
return "healthy";
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Execute custom health checks
|
|
204
|
+
*/
|
|
205
|
+
async function executeCustomChecks(customChecks, timeoutMs) {
|
|
206
|
+
const results = {};
|
|
207
|
+
for (const customCheck of customChecks) try {
|
|
208
|
+
const controller = new AbortController();
|
|
209
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
210
|
+
const result = await Promise.race([customCheck.check(), new Promise((resolve, reject) => {
|
|
211
|
+
setTimeout(() => reject(/* @__PURE__ */ new Error("Timeout")), timeoutMs);
|
|
212
|
+
})]);
|
|
213
|
+
results[customCheck.name] = normalizeCustomCheckResult(result);
|
|
214
|
+
clearTimeout(timeout);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
results[customCheck.name] = {
|
|
217
|
+
status: "down",
|
|
218
|
+
error: error instanceof Error ? error.message : String(error)
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
return results;
|
|
222
|
+
}
|
|
223
|
+
function normalizeCustomCheckResult(value) {
|
|
224
|
+
if (typeof value === "object" && value !== null) return value;
|
|
225
|
+
return {
|
|
226
|
+
status: "down",
|
|
227
|
+
error: "Custom check returned invalid result"
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Main health check function
|
|
232
|
+
* Performs comprehensive health checks on all configured dependencies
|
|
233
|
+
*
|
|
234
|
+
* @param config - Health check configuration
|
|
235
|
+
* @returns Promise<HealthCheckResult> - Comprehensive health check result
|
|
236
|
+
*/
|
|
237
|
+
async function checkHealth(config) {
|
|
238
|
+
const startTime = Date.now();
|
|
239
|
+
const timeoutMs = config.checkTimeoutMs ?? 2e3;
|
|
240
|
+
const version = config.version ?? process.env.npm_package_version ?? "1.0.0";
|
|
241
|
+
const environment = process.env.NODE_ENV ?? "development";
|
|
242
|
+
const [database, redis, aiProvider, memory] = await Promise.all([
|
|
243
|
+
config.enableDatabase !== false ? checkDatabase(config, timeoutMs) : Promise.resolve(void 0),
|
|
244
|
+
config.enableRedis !== false ? checkRedis(config, timeoutMs) : Promise.resolve(void 0),
|
|
245
|
+
config.enableAIProvider !== false ? checkAIProvider(config, timeoutMs) : Promise.resolve(void 0),
|
|
246
|
+
config.enableMemory !== false ? checkMemory(config) : Promise.resolve(void 0)
|
|
247
|
+
]);
|
|
248
|
+
let customChecksResults = {};
|
|
249
|
+
if (config.customChecks && config.customChecks.length > 0) customChecksResults = await executeCustomChecks(config.customChecks, timeoutMs);
|
|
250
|
+
const checks = {};
|
|
251
|
+
if (database !== void 0) checks.database = database;
|
|
252
|
+
if (redis !== void 0) checks.redis = redis;
|
|
253
|
+
if (aiProvider !== void 0) checks.aiProvider = aiProvider;
|
|
254
|
+
if (memory !== void 0) checks.memory = memory;
|
|
255
|
+
Object.assign(checks, customChecksResults);
|
|
256
|
+
const status = determineStatus(database, redis, aiProvider, memory);
|
|
257
|
+
const responseTime = Date.now() - startTime;
|
|
258
|
+
return {
|
|
259
|
+
status,
|
|
260
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
261
|
+
version,
|
|
262
|
+
environment,
|
|
263
|
+
uptime: process.uptime(),
|
|
264
|
+
responseTime,
|
|
265
|
+
checks
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Create a health check request handler
|
|
270
|
+
* Returns a function compatible with Express, Next.js, or other Node.js frameworks
|
|
271
|
+
*
|
|
272
|
+
* @param config - Health check configuration
|
|
273
|
+
* @returns RequestHandler function
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* // Express
|
|
277
|
+
* app.get('/health', createHealthCheckHandler({ enableDatabase: true }));
|
|
278
|
+
*
|
|
279
|
+
* // Next.js Route Handler
|
|
280
|
+
* export const GET = createHealthCheckHandler({ enableDatabase: true });
|
|
281
|
+
*/
|
|
282
|
+
function createHealthCheckHandler(config) {
|
|
283
|
+
return async (req, res) => {
|
|
284
|
+
try {
|
|
285
|
+
const result = await checkHealth(config);
|
|
286
|
+
const statusCode = result.status === "unhealthy" ? 503 : 200;
|
|
287
|
+
res.setHeader("Content-Type", "application/json");
|
|
288
|
+
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
289
|
+
res.status(statusCode).json(result);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
292
|
+
res.setHeader("Content-Type", "application/json");
|
|
293
|
+
res.status(503).json({
|
|
294
|
+
status: "unhealthy",
|
|
295
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
296
|
+
error: config.includeSensitiveDetails ? errorMessage : "Health check failed",
|
|
297
|
+
version: config.version ?? process.env.npm_package_version ?? "1.0.0",
|
|
298
|
+
environment: process.env.NODE_ENV ?? "development"
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Create a health check handler for Next.js 13+ Route Handlers
|
|
305
|
+
* Returns NextResponse compatible handler
|
|
306
|
+
*
|
|
307
|
+
* @param config - Health check configuration
|
|
308
|
+
* @returns Handler function for Next.js Route Handlers
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* // app/api/health/route.ts
|
|
312
|
+
* export const GET = createNextHealthCheckHandler({ enableDatabase: true });
|
|
313
|
+
*/
|
|
314
|
+
function createNextHealthCheckHandler(config) {
|
|
315
|
+
const handler = async () => {
|
|
316
|
+
try {
|
|
317
|
+
const result = await checkHealth(config);
|
|
318
|
+
const statusCode = result.status === "unhealthy" ? 503 : 200;
|
|
319
|
+
return new Response(JSON.stringify(result), {
|
|
320
|
+
status: statusCode,
|
|
321
|
+
headers: { "Content-Type": "application/json" }
|
|
322
|
+
});
|
|
323
|
+
} catch (error) {
|
|
324
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
325
|
+
return new Response(JSON.stringify({
|
|
326
|
+
status: "unhealthy",
|
|
327
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
328
|
+
error: config.includeSensitiveDetails ? errorMessage : "Health check failed",
|
|
329
|
+
version: config.version ?? process.env.npm_package_version ?? "1.0.0",
|
|
330
|
+
environment: process.env.NODE_ENV ?? "development"
|
|
331
|
+
}), {
|
|
332
|
+
status: 503,
|
|
333
|
+
headers: { "Content-Type": "application/json" }
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
return handler;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Express middleware factory for health check endpoint
|
|
341
|
+
*
|
|
342
|
+
* @param config - Health check configuration
|
|
343
|
+
* @returns Express middleware function
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* import express from 'express';
|
|
347
|
+
* import { createHealthCheckMiddleware } from './health-check';
|
|
348
|
+
*
|
|
349
|
+
* const app = express();
|
|
350
|
+
* app.use(createHealthCheckMiddleware({ enableDatabase: true }));
|
|
351
|
+
*/
|
|
352
|
+
function createHealthCheckMiddleware(config) {
|
|
353
|
+
let cachedResult = null;
|
|
354
|
+
let cacheTime = 0;
|
|
355
|
+
const cacheDuration = config.cacheDurationMs ?? 0;
|
|
356
|
+
return async (req, res, next) => {
|
|
357
|
+
if (!req.path?.endsWith("/health") && !req.url?.endsWith("/health")) return next?.();
|
|
358
|
+
try {
|
|
359
|
+
if (cachedResult && cacheDuration > 0) {
|
|
360
|
+
if (Date.now() - cacheTime < cacheDuration) {
|
|
361
|
+
const statusCode = cachedResult.status === "unhealthy" ? 503 : 200;
|
|
362
|
+
return res.status(statusCode).json(cachedResult);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const result = await checkHealth(config);
|
|
366
|
+
if (cacheDuration > 0) {
|
|
367
|
+
cachedResult = result;
|
|
368
|
+
cacheTime = Date.now();
|
|
369
|
+
}
|
|
370
|
+
const statusCode = result.status === "unhealthy" ? 503 : 200;
|
|
371
|
+
res.setHeader("Content-Type", "application/json");
|
|
372
|
+
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
373
|
+
res.status(statusCode).json(result);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
376
|
+
res.setHeader("Content-Type", "application/json");
|
|
377
|
+
res.status(503).json({
|
|
378
|
+
status: "unhealthy",
|
|
379
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
380
|
+
error: config.includeSensitiveDetails ? errorMessage : "Health check failed",
|
|
381
|
+
version: config.version ?? process.env.npm_package_version ?? "1.0.0",
|
|
382
|
+
environment: process.env.NODE_ENV ?? "development"
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
//#endregion
|
|
389
|
+
//#region src/server/messages/extraction.ts
|
|
390
|
+
/**
|
|
391
|
+
* @fileoverview Message extraction and session utilities migrated from @od-oneapp/ai.
|
|
392
|
+
*
|
|
393
|
+
* Provides functions for extracting content from UI messages, validating messages,
|
|
394
|
+
* session initialization, and message persistence helpers.
|
|
395
|
+
*
|
|
396
|
+
* @module @od-oneapp/ai-platform/server/messages/extraction
|
|
397
|
+
*/
|
|
398
|
+
/**
|
|
399
|
+
* Extract text content from a UIMessage by joining all text parts.
|
|
400
|
+
*
|
|
401
|
+
* @param message - UI message to extract text from
|
|
402
|
+
* @returns Concatenated text from all text parts, or empty string
|
|
403
|
+
*
|
|
404
|
+
* @example
|
|
405
|
+
* ```ts
|
|
406
|
+
* const text = extractTextContent(message);
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
function extractTextContent(message) {
|
|
410
|
+
if (!message.parts) return "";
|
|
411
|
+
return message.parts.filter((part) => {
|
|
412
|
+
return part.type === "text" && "text" in part;
|
|
413
|
+
}).map((part) => part.text).join("\n");
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Extract file attachments from a UIMessage.
|
|
417
|
+
*
|
|
418
|
+
* @param message - UI message to extract files from
|
|
419
|
+
* @returns Array of extracted file parts
|
|
420
|
+
*
|
|
421
|
+
* @example
|
|
422
|
+
* ```ts
|
|
423
|
+
* const files = extractFileAttachments(message);
|
|
424
|
+
* ```
|
|
425
|
+
*/
|
|
426
|
+
function extractFileAttachments(message) {
|
|
427
|
+
if (!message.parts) return [];
|
|
428
|
+
return message.parts.filter((part) => {
|
|
429
|
+
return part.type === "file" && "name" in part && "url" in part && "mimeType" in part;
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Find the last user message in an array of messages.
|
|
434
|
+
*
|
|
435
|
+
* @param messages - Array of UIMessage objects
|
|
436
|
+
* @returns The last user message or null if none found
|
|
437
|
+
*
|
|
438
|
+
* @example
|
|
439
|
+
* ```ts
|
|
440
|
+
* const lastUser = getLastUserMessage(messages);
|
|
441
|
+
* if (lastUser) {
|
|
442
|
+
* console.log(extractTextContent(lastUser));
|
|
443
|
+
* }
|
|
444
|
+
* ```
|
|
445
|
+
*/
|
|
446
|
+
function getLastUserMessage(messages) {
|
|
447
|
+
return messages.findLast((message) => message.role === "user") ?? null;
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Basic message validator for UIMessage arrays.
|
|
451
|
+
*
|
|
452
|
+
* @param messages - Unknown value to validate
|
|
453
|
+
* @returns Type guard confirming the value is a UIMessage array
|
|
454
|
+
*/
|
|
455
|
+
function defaultValidateMessages(messages) {
|
|
456
|
+
return Array.isArray(messages);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Extract text from a UIMessage that may contain parts or plain content.
|
|
460
|
+
*
|
|
461
|
+
* @param message - UI message to extract text from
|
|
462
|
+
* @returns Plain text string from the message
|
|
463
|
+
*/
|
|
464
|
+
function defaultGetMessageText(message) {
|
|
465
|
+
if ("content" in message && typeof message.content === "string") return message.content;
|
|
466
|
+
if ("parts" in message && Array.isArray(message.parts)) return message.parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join("");
|
|
467
|
+
return "";
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Minimal default session initializer using in-memory IDs.
|
|
471
|
+
* Consumers can override with database-backed implementations.
|
|
472
|
+
*/
|
|
473
|
+
const inMemoryInitializeSession = async (sessionId) => ({
|
|
474
|
+
sessionId: sessionId ?? generateId(),
|
|
475
|
+
isNew: !sessionId
|
|
476
|
+
});
|
|
477
|
+
/** No-op persistence helper for environments without storage. */
|
|
478
|
+
const noopPersistMessage = async () => {};
|
|
479
|
+
/** No-op resumable stream helper for environments without resumption support. */
|
|
480
|
+
const noopSetupResumableStream = async () => {};
|
|
481
|
+
/**
|
|
482
|
+
* Extract reasoning text from a finish event.
|
|
483
|
+
* Handles both array and string formats for reasoning.
|
|
484
|
+
*
|
|
485
|
+
* @param finishEvent - Finish event from streamText/generateText
|
|
486
|
+
* @returns Reasoning text string or undefined
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* ```ts
|
|
490
|
+
* const reasoning = extractReasoningText(event);
|
|
491
|
+
* if (reasoning) {
|
|
492
|
+
* console.log('Model reasoning:', reasoning);
|
|
493
|
+
* }
|
|
494
|
+
* ```
|
|
495
|
+
*/
|
|
496
|
+
function extractReasoningText(finishEvent) {
|
|
497
|
+
if (!finishEvent.reasoning) return void 0;
|
|
498
|
+
if (Array.isArray(finishEvent.reasoning)) return finishEvent.reasoning.map((r) => r?.text ?? "").join("");
|
|
499
|
+
if (typeof finishEvent.reasoning === "string") return finishEvent.reasoning;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Build UI message parts from AI SDK finish event.
|
|
503
|
+
* Extracts text, reasoning, tool calls, and tool results from the finish event.
|
|
504
|
+
*
|
|
505
|
+
* @param finishEvent - Finish event from streamText/generateText
|
|
506
|
+
* @returns Array of UI message parts
|
|
507
|
+
*/
|
|
508
|
+
function buildUIMessagePartsFromFinishEvent(finishEvent) {
|
|
509
|
+
const parts = [];
|
|
510
|
+
if (finishEvent.text) parts.push({
|
|
511
|
+
type: "text",
|
|
512
|
+
text: finishEvent.text
|
|
513
|
+
});
|
|
514
|
+
if (finishEvent.reasoning && Array.isArray(finishEvent.reasoning)) {
|
|
515
|
+
const reasoningText = finishEvent.reasoning.map((r) => r?.text ?? "").join("");
|
|
516
|
+
if (reasoningText) parts.push({
|
|
517
|
+
type: "reasoning",
|
|
518
|
+
text: reasoningText
|
|
519
|
+
});
|
|
520
|
+
} else if (finishEvent.reasoning && typeof finishEvent.reasoning === "string") parts.push({
|
|
521
|
+
type: "reasoning",
|
|
522
|
+
text: finishEvent.reasoning
|
|
523
|
+
});
|
|
524
|
+
if (finishEvent.toolCalls && finishEvent.toolCalls.length > 0) for (const tc of finishEvent.toolCalls) {
|
|
525
|
+
const args = "args" in tc ? tc.args : tc.input;
|
|
526
|
+
parts.push({
|
|
527
|
+
type: "tool-invocation",
|
|
528
|
+
toolCallId: tc.toolCallId,
|
|
529
|
+
toolName: tc.toolName,
|
|
530
|
+
args: args ?? {}
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
if (finishEvent.toolResults && finishEvent.toolResults.length > 0) for (const tr of finishEvent.toolResults) {
|
|
534
|
+
const result = "result" in tr ? tr.result : tr.output;
|
|
535
|
+
parts.push({
|
|
536
|
+
type: "tool-result",
|
|
537
|
+
toolCallId: tr.toolCallId,
|
|
538
|
+
toolName: tr.toolName,
|
|
539
|
+
result: result ?? null
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
return parts;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Map stored message to UI message format.
|
|
546
|
+
* Converts database-stored message format to AI SDK UIMessage format.
|
|
547
|
+
*
|
|
548
|
+
* @param stored - Stored message from database
|
|
549
|
+
* @returns UIMessage compatible with AI SDK
|
|
550
|
+
*/
|
|
551
|
+
function mapStoredToUIMessage(stored) {
|
|
552
|
+
return {
|
|
553
|
+
id: stored.id,
|
|
554
|
+
role: stored.role,
|
|
555
|
+
parts: Array.isArray(stored.parts) && stored.parts.length > 0 ? stored.parts : stored.content ? [{
|
|
556
|
+
type: "text",
|
|
557
|
+
text: stored.content
|
|
558
|
+
}] : []
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Map array of stored messages to UI messages format.
|
|
563
|
+
*
|
|
564
|
+
* @param stored - Array of stored messages from database
|
|
565
|
+
* @returns Array of UIMessage compatible with AI SDK
|
|
566
|
+
*/
|
|
567
|
+
function mapStoredMessagesToUI(stored) {
|
|
568
|
+
return stored.map(mapStoredToUIMessage);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
//#endregion
|
|
572
|
+
//#region src/server/routes/agent-routes.ts
|
|
573
|
+
/**
|
|
574
|
+
* @fileoverview Agent route handler factory for Next.js/Fetch-compatible routes.
|
|
575
|
+
*
|
|
576
|
+
* Creates POST handlers that handle request parsing, session initialization,
|
|
577
|
+
* user message persistence, agent execution, and optional stream resumption.
|
|
578
|
+
*
|
|
579
|
+
* Migrated from @od-oneapp/ai/server/agent-routes.ts
|
|
580
|
+
*
|
|
581
|
+
* @module @od-oneapp/ai-platform/server/routes/agent-routes
|
|
582
|
+
*/
|
|
583
|
+
/**
|
|
584
|
+
* Create a JSON error response.
|
|
585
|
+
* Consistent error format for agent route handlers.
|
|
586
|
+
*/
|
|
587
|
+
function jsonError$1(error, status = 400) {
|
|
588
|
+
return new Response(JSON.stringify({ error }), {
|
|
589
|
+
status,
|
|
590
|
+
headers: { "Content-Type": "application/json" }
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Create a Next.js/Fetch-compatible POST handler for AI SDK agents.
|
|
595
|
+
*
|
|
596
|
+
* Handles request parsing, session initialization, user message persistence,
|
|
597
|
+
* agent execution, and optional stream resumption.
|
|
598
|
+
*
|
|
599
|
+
* @param options - Route handler configuration
|
|
600
|
+
* @returns POST handler function
|
|
601
|
+
*
|
|
602
|
+
* @example
|
|
603
|
+
* ```ts
|
|
604
|
+
* import { createAgentRouteHandler } from '@od-oneapp/ai-platform/server';
|
|
605
|
+
*
|
|
606
|
+
* const POST = createAgentRouteHandler({
|
|
607
|
+
* agent: myToolLoopAgent,
|
|
608
|
+
* sendReasoning: true,
|
|
609
|
+
* initializeSession: myDbSessionInit,
|
|
610
|
+
* persistUserMessage: myDbPersist,
|
|
611
|
+
* });
|
|
612
|
+
*
|
|
613
|
+
* export { POST };
|
|
614
|
+
* ```
|
|
615
|
+
*/
|
|
616
|
+
function createAgentRouteHandler(options) {
|
|
617
|
+
const { agent, execute, sendReasoning = true, onError, resolveOptions, initializeSession = inMemoryInitializeSession, persistUserMessage, setupResumableStream, validateMessages = defaultValidateMessages, getMessageText = defaultGetMessageText } = options;
|
|
618
|
+
return async function POST(req) {
|
|
619
|
+
const body = await req.json().catch(() => null);
|
|
620
|
+
if (!body || typeof body !== "object") return jsonError$1("Invalid request body", 400);
|
|
621
|
+
const { messages, id, ...rest } = body;
|
|
622
|
+
if (!validateMessages(messages)) return jsonError$1("Invalid request: messages must be an array", 400);
|
|
623
|
+
const uiMessages = messages;
|
|
624
|
+
const { sessionId, isNew } = await initializeSession(id);
|
|
625
|
+
if (persistUserMessage && uiMessages.length > 0) {
|
|
626
|
+
const lastUser = typeof uiMessages.findLast === "function" ? uiMessages.findLast((m) => m.role === "user") : [...uiMessages].reverse().find((m) => m.role === "user");
|
|
627
|
+
if (lastUser) {
|
|
628
|
+
const text = getMessageText(lastUser);
|
|
629
|
+
if (text) await persistUserMessage(sessionId, text, { autoTitle: isNew });
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
let response;
|
|
633
|
+
if (execute) {
|
|
634
|
+
const bodyObj = {
|
|
635
|
+
messages: uiMessages,
|
|
636
|
+
...rest
|
|
637
|
+
};
|
|
638
|
+
if (id !== void 0) bodyObj.id = id;
|
|
639
|
+
response = await execute({
|
|
640
|
+
messages: uiMessages,
|
|
641
|
+
sessionId,
|
|
642
|
+
isNew,
|
|
643
|
+
body: bodyObj
|
|
644
|
+
});
|
|
645
|
+
} else if (agent) response = await createAgentUIStreamResponse({
|
|
646
|
+
agent,
|
|
647
|
+
uiMessages,
|
|
648
|
+
sendReasoning,
|
|
649
|
+
options: resolveOptions?.({
|
|
650
|
+
body: {
|
|
651
|
+
messages: uiMessages,
|
|
652
|
+
id,
|
|
653
|
+
...rest
|
|
654
|
+
},
|
|
655
|
+
messages: uiMessages,
|
|
656
|
+
sessionId
|
|
657
|
+
}),
|
|
658
|
+
onError: onError ? (error) => {
|
|
659
|
+
return onError(error, { sessionId }) ?? "An error occurred";
|
|
660
|
+
} : void 0
|
|
661
|
+
});
|
|
662
|
+
else return jsonError$1("No agent or execute handler provided", 500);
|
|
663
|
+
if (setupResumableStream) await setupResumableStream(response, sessionId);
|
|
664
|
+
return response;
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
//#endregion
|
|
669
|
+
//#region src/server/messages/converter.ts
|
|
670
|
+
/**
|
|
671
|
+
* Default configuration for message converter.
|
|
672
|
+
*/
|
|
673
|
+
const DEFAULT_CONFIG$1 = {
|
|
674
|
+
stripSystemMessages: false,
|
|
675
|
+
maxMessages: Infinity,
|
|
676
|
+
transform: void 0
|
|
677
|
+
};
|
|
678
|
+
/**
|
|
679
|
+
* Creates a message converter for transforming UI messages to model format.
|
|
680
|
+
*
|
|
681
|
+
* The converter applies the following transformations in order:
|
|
682
|
+
* 1. Custom transform function (if provided)
|
|
683
|
+
* 2. System message filtering (if stripSystemMessages is true)
|
|
684
|
+
* 3. Sliding window truncation (if maxMessages is set)
|
|
685
|
+
* 4. Conversion to model message format
|
|
686
|
+
*
|
|
687
|
+
* @param config - Converter configuration options.
|
|
688
|
+
* @returns MessageConverter instance.
|
|
689
|
+
*
|
|
690
|
+
* @example Basic usage
|
|
691
|
+
* ```ts
|
|
692
|
+
* const converter = createMessageConverter();
|
|
693
|
+
* const modelMessages = await converter.toModelMessages(uiMessages);
|
|
694
|
+
* ```
|
|
695
|
+
*
|
|
696
|
+
* @example With configuration
|
|
697
|
+
* ```ts
|
|
698
|
+
* const converter = createMessageConverter({
|
|
699
|
+
* maxMessages: 20,
|
|
700
|
+
* stripSystemMessages: true,
|
|
701
|
+
* });
|
|
702
|
+
*
|
|
703
|
+
* // Only last 20 non-system messages will be included
|
|
704
|
+
* const modelMessages = await converter.toModelMessages(uiMessages);
|
|
705
|
+
* ```
|
|
706
|
+
*
|
|
707
|
+
* @example With custom transform
|
|
708
|
+
* ```ts
|
|
709
|
+
* const converter = createMessageConverter({
|
|
710
|
+
* transform: (msg) => {
|
|
711
|
+
* // Filter out empty messages
|
|
712
|
+
* if (!msg.content.trim()) return null;
|
|
713
|
+
* // Normalize content
|
|
714
|
+
* return { ...msg, content: msg.content.toLowerCase() };
|
|
715
|
+
* },
|
|
716
|
+
* });
|
|
717
|
+
* ```
|
|
718
|
+
*/
|
|
719
|
+
function createMessageConverter(config = {}) {
|
|
720
|
+
const mergedConfig = {
|
|
721
|
+
...DEFAULT_CONFIG$1,
|
|
722
|
+
...config
|
|
723
|
+
};
|
|
724
|
+
/**
|
|
725
|
+
* Applies all transformations to the message array.
|
|
726
|
+
* @param messages - Input messages.
|
|
727
|
+
* @returns Transformed messages.
|
|
728
|
+
*/
|
|
729
|
+
function processMessages(messages) {
|
|
730
|
+
let processed = [...messages];
|
|
731
|
+
if (mergedConfig.transform) {
|
|
732
|
+
const { transform } = mergedConfig;
|
|
733
|
+
processed = processed.map((msg) => transform(msg)).filter((msg) => msg !== null);
|
|
734
|
+
}
|
|
735
|
+
if (mergedConfig.stripSystemMessages) processed = processed.filter((msg) => msg.role !== "system");
|
|
736
|
+
if (mergedConfig.maxMessages < Infinity && processed.length > mergedConfig.maxMessages) processed = processed.slice(-mergedConfig.maxMessages);
|
|
737
|
+
return processed;
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Converts a single UI message to model message format.
|
|
741
|
+
* @param uiMessage - UI message to convert.
|
|
742
|
+
* @returns Model message.
|
|
743
|
+
*/
|
|
744
|
+
function convertToModelMessage(uiMessage) {
|
|
745
|
+
return {
|
|
746
|
+
role: uiMessage.role,
|
|
747
|
+
content: uiMessage.content
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
return {
|
|
751
|
+
async toModelMessages(messages) {
|
|
752
|
+
return processMessages(messages).map(convertToModelMessage);
|
|
753
|
+
},
|
|
754
|
+
toModelMessagesSync(messages) {
|
|
755
|
+
return processMessages(messages).map(convertToModelMessage);
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Creates a transform function that chains multiple transforms.
|
|
761
|
+
*
|
|
762
|
+
* @param transforms - Array of transform functions to chain.
|
|
763
|
+
* @returns Combined transform function.
|
|
764
|
+
*
|
|
765
|
+
* @example
|
|
766
|
+
* ```ts
|
|
767
|
+
* const converter = createMessageConverter({
|
|
768
|
+
* transform: chainTransforms([
|
|
769
|
+
* // Remove empty messages
|
|
770
|
+
* (msg) => msg.content.trim() ? msg : null,
|
|
771
|
+
* // Normalize whitespace
|
|
772
|
+
* (msg) => ({ ...msg, content: msg.content.replace(/\s+/g, ' ') }),
|
|
773
|
+
* ]),
|
|
774
|
+
* });
|
|
775
|
+
* ```
|
|
776
|
+
*/
|
|
777
|
+
function chainTransforms(transforms) {
|
|
778
|
+
return (msg) => {
|
|
779
|
+
let current = msg;
|
|
780
|
+
for (const transform of transforms) {
|
|
781
|
+
if (current === null) return null;
|
|
782
|
+
current = transform(current);
|
|
783
|
+
}
|
|
784
|
+
return current;
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Creates a transform that filters messages by role.
|
|
789
|
+
*
|
|
790
|
+
* @param allowedRoles - Roles to allow through.
|
|
791
|
+
* @returns Transform function.
|
|
792
|
+
*
|
|
793
|
+
* @example
|
|
794
|
+
* ```ts
|
|
795
|
+
* const converter = createMessageConverter({
|
|
796
|
+
* transform: filterByRole(['user', 'assistant']),
|
|
797
|
+
* });
|
|
798
|
+
* ```
|
|
799
|
+
*/
|
|
800
|
+
function filterByRole(allowedRoles) {
|
|
801
|
+
const roleSet = new Set(allowedRoles);
|
|
802
|
+
return (msg) => roleSet.has(msg.role) ? msg : null;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* Creates a transform that filters messages by content.
|
|
806
|
+
*
|
|
807
|
+
* @param predicate - Function to test message content.
|
|
808
|
+
* @returns Transform function.
|
|
809
|
+
*
|
|
810
|
+
* @example
|
|
811
|
+
* ```ts
|
|
812
|
+
* // Filter out messages with only whitespace
|
|
813
|
+
* const converter = createMessageConverter({
|
|
814
|
+
* transform: filterByContent((content) => content.trim().length > 0),
|
|
815
|
+
* });
|
|
816
|
+
* ```
|
|
817
|
+
*/
|
|
818
|
+
function filterByContent(predicate) {
|
|
819
|
+
return (msg) => predicate(msg.content) ? msg : null;
|
|
820
|
+
}
|
|
821
|
+
/**
|
|
822
|
+
* Creates a transform that modifies message content.
|
|
823
|
+
*
|
|
824
|
+
* @param modifier - Function to modify content.
|
|
825
|
+
* @returns Transform function.
|
|
826
|
+
*
|
|
827
|
+
* @example
|
|
828
|
+
* ```ts
|
|
829
|
+
* // Trim all message content
|
|
830
|
+
* const converter = createMessageConverter({
|
|
831
|
+
* transform: transformContent((content) => content.trim()),
|
|
832
|
+
* });
|
|
833
|
+
* ```
|
|
834
|
+
*/
|
|
835
|
+
function transformContent(modifier) {
|
|
836
|
+
return (msg) => ({
|
|
837
|
+
...msg,
|
|
838
|
+
content: modifier(msg.content)
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
//#endregion
|
|
843
|
+
//#region src/server/messages/data-parts.ts
|
|
844
|
+
/**
|
|
845
|
+
* @fileoverview Data part extractor factory for type-safe data extraction.
|
|
846
|
+
* @module @od-oneapp/ai-platform/messages
|
|
847
|
+
*
|
|
848
|
+
* Provides factory for creating typed data part extractors that validate
|
|
849
|
+
* and extract custom data from message streams.
|
|
850
|
+
*
|
|
851
|
+
* @example
|
|
852
|
+
* ```ts
|
|
853
|
+
* import { z } from 'zod';
|
|
854
|
+
* import { createDataPartExtractor } from '@od-oneapp/ai-platform/messages';
|
|
855
|
+
*
|
|
856
|
+
* const ToolShimmerSchema = z.object({
|
|
857
|
+
* toolCallId: z.string(),
|
|
858
|
+
* toolName: z.string(),
|
|
859
|
+
* state: z.enum(['pending', 'loading', 'success', 'error']),
|
|
860
|
+
* });
|
|
861
|
+
*
|
|
862
|
+
* const ProgressSchema = z.object({
|
|
863
|
+
* current: z.number(),
|
|
864
|
+
* total: z.number(),
|
|
865
|
+
* });
|
|
866
|
+
*
|
|
867
|
+
* const extractor = createDataPartExtractor({
|
|
868
|
+
* schemas: {
|
|
869
|
+
* 'tool-shimmer': ToolShimmerSchema,
|
|
870
|
+
* 'progress': ProgressSchema,
|
|
871
|
+
* },
|
|
872
|
+
* onUnknownType: (type, data) => {
|
|
873
|
+
* console.warn(`Unknown data part type: ${type}`);
|
|
874
|
+
* },
|
|
875
|
+
* });
|
|
876
|
+
*
|
|
877
|
+
* const shimmers = extractor.extract(messages, 'tool-shimmer');
|
|
878
|
+
* const allParts = extractor.extractAll(messages);
|
|
879
|
+
* ```
|
|
880
|
+
*/
|
|
881
|
+
/**
|
|
882
|
+
* Creates a type-safe data part extractor with Zod schema validation.
|
|
883
|
+
*
|
|
884
|
+
* The extractor validates each data part against its registered schema,
|
|
885
|
+
* providing type-safe access to custom streaming data.
|
|
886
|
+
*
|
|
887
|
+
* @param config - Extractor configuration with schema map.
|
|
888
|
+
* @returns DataPartExtractor instance.
|
|
889
|
+
*
|
|
890
|
+
* @example Basic usage
|
|
891
|
+
* ```ts
|
|
892
|
+
* import { z } from 'zod';
|
|
893
|
+
*
|
|
894
|
+
* const SourceSchema = z.object({
|
|
895
|
+
* id: z.string(),
|
|
896
|
+
* url: z.string().url(),
|
|
897
|
+
* title: z.string(),
|
|
898
|
+
* });
|
|
899
|
+
*
|
|
900
|
+
* const extractor = createDataPartExtractor({
|
|
901
|
+
* schemas: { source: SourceSchema },
|
|
902
|
+
* });
|
|
903
|
+
*
|
|
904
|
+
* // Type-safe extraction - returns Source[]
|
|
905
|
+
* const sources = extractor.extract(messages, 'source');
|
|
906
|
+
* ```
|
|
907
|
+
*
|
|
908
|
+
* @example With unknown type handler
|
|
909
|
+
* ```ts
|
|
910
|
+
* const extractor = createDataPartExtractor({
|
|
911
|
+
* schemas: { progress: ProgressSchema },
|
|
912
|
+
* onUnknownType: (type, data) => {
|
|
913
|
+
* // Log unknown types for debugging
|
|
914
|
+
* console.warn(`Unhandled data part: ${type}`, data);
|
|
915
|
+
* },
|
|
916
|
+
* });
|
|
917
|
+
* ```
|
|
918
|
+
*
|
|
919
|
+
* @example Extracting all types
|
|
920
|
+
* ```ts
|
|
921
|
+
* const allParts = extractor.extractAll(messages);
|
|
922
|
+
* // { progress: [...], source: [...] }
|
|
923
|
+
* ```
|
|
924
|
+
*/
|
|
925
|
+
function createDataPartExtractor(config) {
|
|
926
|
+
const { schemas, onUnknownType } = config;
|
|
927
|
+
const schemaKeys = new Set(Object.keys(schemas));
|
|
928
|
+
/**
|
|
929
|
+
* Extracts data parts from a single message.
|
|
930
|
+
* @param message - Message to extract from.
|
|
931
|
+
* @returns Array of data parts.
|
|
932
|
+
*/
|
|
933
|
+
function extractFromMessage(message) {
|
|
934
|
+
const parts = [];
|
|
935
|
+
if (message.data && Array.isArray(message.data)) parts.push(...message.data);
|
|
936
|
+
if (message.parts && Array.isArray(message.parts)) {
|
|
937
|
+
for (const part of message.parts) if (part && typeof part === "object" && "type" in part && "data" in part && typeof part.type === "string") parts.push(part);
|
|
938
|
+
}
|
|
939
|
+
return parts;
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Validates a data part against its schema.
|
|
943
|
+
* @param type - Type of the data part.
|
|
944
|
+
* @param data - Data to validate.
|
|
945
|
+
* @returns Validated data or null if invalid.
|
|
946
|
+
*/
|
|
947
|
+
function validateDataPart(type, data) {
|
|
948
|
+
const schema = schemas[type];
|
|
949
|
+
if (!schema) return null;
|
|
950
|
+
const result = schema.safeParse(data);
|
|
951
|
+
if (result.success) return result.data;
|
|
952
|
+
if (process.env.NODE_ENV === "development") logWarn(`Data part validation failed for type "${type}"`, { error: result.error });
|
|
953
|
+
return null;
|
|
954
|
+
}
|
|
955
|
+
return {
|
|
956
|
+
extract(messages, type) {
|
|
957
|
+
const results = [];
|
|
958
|
+
for (const message of messages) {
|
|
959
|
+
const parts = extractFromMessage(message);
|
|
960
|
+
for (const part of parts) if (part.type === type) {
|
|
961
|
+
const validated = validateDataPart(type, part.data);
|
|
962
|
+
if (validated !== null) results.push(validated);
|
|
963
|
+
} else if (!schemaKeys.has(part.type) && onUnknownType) onUnknownType(part.type, part.data);
|
|
964
|
+
}
|
|
965
|
+
return results;
|
|
966
|
+
},
|
|
967
|
+
extractAll(messages) {
|
|
968
|
+
const results = {};
|
|
969
|
+
for (const type of schemaKeys) results[type] = [];
|
|
970
|
+
for (const message of messages) {
|
|
971
|
+
const parts = extractFromMessage(message);
|
|
972
|
+
for (const part of parts) if (schemaKeys.has(part.type)) {
|
|
973
|
+
const type = part.type;
|
|
974
|
+
const validated = validateDataPart(type, part.data);
|
|
975
|
+
if (validated !== null) results[type]?.push(validated);
|
|
976
|
+
} else if (onUnknownType) onUnknownType(part.type, part.data);
|
|
977
|
+
}
|
|
978
|
+
return results;
|
|
979
|
+
},
|
|
980
|
+
hasSchema(type) {
|
|
981
|
+
return schemaKeys.has(type);
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Creates a combined extractor from multiple schema maps.
|
|
987
|
+
*
|
|
988
|
+
* @param extractors - Array of extractor configs to combine.
|
|
989
|
+
* @returns Combined DataPartExtractorConfig.
|
|
990
|
+
*
|
|
991
|
+
* @example
|
|
992
|
+
* ```ts
|
|
993
|
+
* const combinedSchemas = mergeSchemas(
|
|
994
|
+
* { progress: ProgressSchema },
|
|
995
|
+
* { source: SourceSchema, step: StepSchema },
|
|
996
|
+
* );
|
|
997
|
+
*
|
|
998
|
+
* const extractor = createDataPartExtractor({
|
|
999
|
+
* schemas: combinedSchemas,
|
|
1000
|
+
* });
|
|
1001
|
+
* ```
|
|
1002
|
+
*/
|
|
1003
|
+
function mergeSchemas(schemas1, schemas2) {
|
|
1004
|
+
return {
|
|
1005
|
+
...schemas1,
|
|
1006
|
+
...schemas2
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Collects all data parts from messages without validation.
|
|
1011
|
+
*
|
|
1012
|
+
* @param messages - Messages to collect from.
|
|
1013
|
+
* @returns Array of all data parts.
|
|
1014
|
+
*
|
|
1015
|
+
* @example
|
|
1016
|
+
* ```ts
|
|
1017
|
+
* const allParts = collectDataParts(messages);
|
|
1018
|
+
* // [{ type: 'progress', data: {...} }, { type: 'source', data: {...} }]
|
|
1019
|
+
* ```
|
|
1020
|
+
*/
|
|
1021
|
+
function collectDataParts(messages) {
|
|
1022
|
+
const parts = [];
|
|
1023
|
+
for (const message of messages) {
|
|
1024
|
+
if (message.data && Array.isArray(message.data)) parts.push(...message.data);
|
|
1025
|
+
if (message.parts && Array.isArray(message.parts)) {
|
|
1026
|
+
for (const part of message.parts) if (part && typeof part === "object" && "type" in part && "data" in part && typeof part.type === "string") parts.push(part);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
return parts;
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Groups data parts by type.
|
|
1033
|
+
*
|
|
1034
|
+
* @param parts - Data parts to group.
|
|
1035
|
+
* @returns Object with data parts grouped by type.
|
|
1036
|
+
*
|
|
1037
|
+
* @example
|
|
1038
|
+
* ```ts
|
|
1039
|
+
* const grouped = groupDataPartsByType(parts);
|
|
1040
|
+
* // { 'progress': [...], 'source': [...] }
|
|
1041
|
+
* ```
|
|
1042
|
+
*/
|
|
1043
|
+
function groupDataPartsByType(parts) {
|
|
1044
|
+
const grouped = {};
|
|
1045
|
+
for (const part of parts) {
|
|
1046
|
+
const bucket = grouped[part.type] ?? [];
|
|
1047
|
+
bucket.push(part);
|
|
1048
|
+
grouped[part.type] = bucket;
|
|
1049
|
+
}
|
|
1050
|
+
return grouped;
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Gets the latest data part of a specific type.
|
|
1054
|
+
*
|
|
1055
|
+
* @param messages - Messages to search.
|
|
1056
|
+
* @param type - Type of data part to find.
|
|
1057
|
+
* @returns Latest data part of the type or undefined.
|
|
1058
|
+
*
|
|
1059
|
+
* @example
|
|
1060
|
+
* ```ts
|
|
1061
|
+
* const latestProgress = getLatestDataPart(messages, 'progress');
|
|
1062
|
+
* ```
|
|
1063
|
+
*/
|
|
1064
|
+
function getLatestDataPart(messages, type) {
|
|
1065
|
+
const parts = collectDataParts(messages);
|
|
1066
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
1067
|
+
const part = parts[i];
|
|
1068
|
+
if (part?.type === type) return part;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
//#endregion
|
|
1073
|
+
//#region src/server/messages/window.ts
|
|
1074
|
+
/**
|
|
1075
|
+
* @fileoverview Message window factory for token-based message truncation.
|
|
1076
|
+
* @module @od-oneapp/ai-platform/messages
|
|
1077
|
+
*
|
|
1078
|
+
* Provides factory for creating message windows that manage conversation
|
|
1079
|
+
* history within token limits.
|
|
1080
|
+
*
|
|
1081
|
+
* @example
|
|
1082
|
+
* ```ts
|
|
1083
|
+
* import { createMessageWindow } from '@od-oneapp/ai-platform/messages';
|
|
1084
|
+
*
|
|
1085
|
+
* const window = createMessageWindow({
|
|
1086
|
+
* maxTokens: 8000,
|
|
1087
|
+
* preserveSystemMessage: true,
|
|
1088
|
+
* strategy: 'fifo',
|
|
1089
|
+
* });
|
|
1090
|
+
*
|
|
1091
|
+
* const truncated = await window.apply(messages);
|
|
1092
|
+
* const tokens = window.estimateTokens(messages);
|
|
1093
|
+
* ```
|
|
1094
|
+
*/
|
|
1095
|
+
/**
|
|
1096
|
+
* Default configuration for message window.
|
|
1097
|
+
*/
|
|
1098
|
+
const DEFAULT_CONFIG = {
|
|
1099
|
+
maxTokens: 4096,
|
|
1100
|
+
tokenizer: "cl100k_base",
|
|
1101
|
+
preserveSystemMessage: true,
|
|
1102
|
+
strategy: "fifo",
|
|
1103
|
+
summarizer: void 0
|
|
1104
|
+
};
|
|
1105
|
+
/**
|
|
1106
|
+
* Estimates tokens per character for different tokenizers.
|
|
1107
|
+
* These are approximations based on typical encoding ratios.
|
|
1108
|
+
*/
|
|
1109
|
+
const CHARS_PER_TOKEN = {
|
|
1110
|
+
cl100k_base: 4,
|
|
1111
|
+
p50k_base: 4,
|
|
1112
|
+
default: 4
|
|
1113
|
+
};
|
|
1114
|
+
/**
|
|
1115
|
+
* Creates a tokenizer function from a tokenizer type.
|
|
1116
|
+
*
|
|
1117
|
+
* @param tokenizer - Tokenizer type or custom function.
|
|
1118
|
+
* @returns Tokenizer function.
|
|
1119
|
+
*/
|
|
1120
|
+
function createTokenizer(tokenizer) {
|
|
1121
|
+
if (typeof tokenizer === "function") return tokenizer;
|
|
1122
|
+
const charsPerToken = CHARS_PER_TOKEN[tokenizer] ?? CHARS_PER_TOKEN.default ?? 4;
|
|
1123
|
+
return (text) => {
|
|
1124
|
+
return Math.ceil(text.length / charsPerToken) + 4;
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
/**
|
|
1128
|
+
* Simple word-based tokenizer for rough estimates.
|
|
1129
|
+
* Counts words and adds overhead.
|
|
1130
|
+
*
|
|
1131
|
+
* @param text - Text to estimate tokens for.
|
|
1132
|
+
* @returns Estimated token count.
|
|
1133
|
+
*/
|
|
1134
|
+
function wordBasedTokenizer(text) {
|
|
1135
|
+
const words = text.split(/\s+/).filter((w) => w.length > 0);
|
|
1136
|
+
return Math.ceil(words.length * 1.3) + 4;
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Creates a message window for managing conversation history within token limits.
|
|
1140
|
+
*
|
|
1141
|
+
* The window applies truncation strategies to keep messages within the
|
|
1142
|
+
* configured token budget while optionally preserving system messages.
|
|
1143
|
+
*
|
|
1144
|
+
* @param config - Window configuration options.
|
|
1145
|
+
* @returns MessageWindow instance.
|
|
1146
|
+
*
|
|
1147
|
+
* @example Basic FIFO truncation
|
|
1148
|
+
* ```ts
|
|
1149
|
+
* const window = createMessageWindow({
|
|
1150
|
+
* maxTokens: 4000,
|
|
1151
|
+
* });
|
|
1152
|
+
*
|
|
1153
|
+
* const truncated = await window.apply(messages);
|
|
1154
|
+
* // Oldest messages removed to fit within 4000 tokens
|
|
1155
|
+
* ```
|
|
1156
|
+
*
|
|
1157
|
+
* @example Preserve system message
|
|
1158
|
+
* ```ts
|
|
1159
|
+
* const window = createMessageWindow({
|
|
1160
|
+
* maxTokens: 8000,
|
|
1161
|
+
* preserveSystemMessage: true,
|
|
1162
|
+
* });
|
|
1163
|
+
*
|
|
1164
|
+
* // System message always included, other messages truncated as needed
|
|
1165
|
+
* const truncated = await window.apply(messages);
|
|
1166
|
+
* ```
|
|
1167
|
+
*
|
|
1168
|
+
* @example Custom tokenizer
|
|
1169
|
+
* ```ts
|
|
1170
|
+
* import { encodingForModel } from 'js-tiktoken';
|
|
1171
|
+
*
|
|
1172
|
+
* const encoding = encodingForModel('gpt-4');
|
|
1173
|
+
*
|
|
1174
|
+
* const window = createMessageWindow({
|
|
1175
|
+
* maxTokens: 8000,
|
|
1176
|
+
* tokenizer: (text) => encoding.encode(text).length,
|
|
1177
|
+
* });
|
|
1178
|
+
* ```
|
|
1179
|
+
*
|
|
1180
|
+
* @example Summarize strategy
|
|
1181
|
+
* ```ts
|
|
1182
|
+
* const window = createMessageWindow({
|
|
1183
|
+
* maxTokens: 8000,
|
|
1184
|
+
* strategy: 'summarize',
|
|
1185
|
+
* summarizer: async (messages) => {
|
|
1186
|
+
* // Use LLM to summarize older messages
|
|
1187
|
+
* const summary = await generateSummary(messages);
|
|
1188
|
+
* return { role: 'system', content: `Previous context: ${summary}` };
|
|
1189
|
+
* },
|
|
1190
|
+
* });
|
|
1191
|
+
* ```
|
|
1192
|
+
*/
|
|
1193
|
+
function createMessageWindow(config) {
|
|
1194
|
+
const mergedConfig = {
|
|
1195
|
+
...DEFAULT_CONFIG,
|
|
1196
|
+
...config
|
|
1197
|
+
};
|
|
1198
|
+
const tokenize = createTokenizer(mergedConfig.tokenizer);
|
|
1199
|
+
/**
|
|
1200
|
+
* Estimates tokens for a single message.
|
|
1201
|
+
* @param message - Message to estimate.
|
|
1202
|
+
* @returns Estimated token count.
|
|
1203
|
+
*/
|
|
1204
|
+
function estimateMessageTokens(message) {
|
|
1205
|
+
let { content } = message;
|
|
1206
|
+
if (message.parts) {
|
|
1207
|
+
for (const part of message.parts) if ("text" in part && typeof part.text === "string") content += ` ${part.text}`;
|
|
1208
|
+
}
|
|
1209
|
+
return tokenize(content);
|
|
1210
|
+
}
|
|
1211
|
+
/**
|
|
1212
|
+
* Estimates total tokens for an array of messages.
|
|
1213
|
+
* @param messages - Messages to estimate.
|
|
1214
|
+
* @returns Total estimated token count.
|
|
1215
|
+
*/
|
|
1216
|
+
function estimateTokens(messages) {
|
|
1217
|
+
let total = 0;
|
|
1218
|
+
for (const message of messages) total += estimateMessageTokens(message);
|
|
1219
|
+
return total;
|
|
1220
|
+
}
|
|
1221
|
+
/**
|
|
1222
|
+
* Applies FIFO truncation strategy.
|
|
1223
|
+
* Removes oldest messages first, optionally preserving system message.
|
|
1224
|
+
* @param messages - Messages to truncate.
|
|
1225
|
+
* @returns Truncated messages.
|
|
1226
|
+
*/
|
|
1227
|
+
function applyFIFO(messages) {
|
|
1228
|
+
if (messages.length === 0) return messages;
|
|
1229
|
+
let systemMessage;
|
|
1230
|
+
let nonSystemMessages;
|
|
1231
|
+
if (mergedConfig.preserveSystemMessage) {
|
|
1232
|
+
systemMessage = messages.find((m) => m.role === "system");
|
|
1233
|
+
nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
1234
|
+
} else nonSystemMessages = [...messages];
|
|
1235
|
+
const systemTokens = systemMessage ? estimateMessageTokens(systemMessage) : 0;
|
|
1236
|
+
const availableTokens = mergedConfig.maxTokens - systemTokens;
|
|
1237
|
+
if (availableTokens <= 0 && systemMessage) return [systemMessage];
|
|
1238
|
+
const result = [];
|
|
1239
|
+
let currentTokens = 0;
|
|
1240
|
+
for (let i = nonSystemMessages.length - 1; i >= 0; i--) {
|
|
1241
|
+
const message = nonSystemMessages[i];
|
|
1242
|
+
if (!message) continue;
|
|
1243
|
+
const messageTokens = estimateMessageTokens(message);
|
|
1244
|
+
if (currentTokens + messageTokens <= availableTokens) {
|
|
1245
|
+
result.unshift(message);
|
|
1246
|
+
currentTokens += messageTokens;
|
|
1247
|
+
} else break;
|
|
1248
|
+
}
|
|
1249
|
+
if (systemMessage) result.unshift(systemMessage);
|
|
1250
|
+
return result;
|
|
1251
|
+
}
|
|
1252
|
+
/**
|
|
1253
|
+
* Applies summarize truncation strategy.
|
|
1254
|
+
* Summarizes older messages when they would be truncated.
|
|
1255
|
+
* @param messages - Messages to process.
|
|
1256
|
+
* @returns Processed messages with summaries.
|
|
1257
|
+
*/
|
|
1258
|
+
async function applySummarize(messages) {
|
|
1259
|
+
if (!mergedConfig.summarizer) return applyFIFO(messages);
|
|
1260
|
+
if (estimateTokens(messages) <= mergedConfig.maxTokens) return messages;
|
|
1261
|
+
let systemMessage;
|
|
1262
|
+
let nonSystemMessages;
|
|
1263
|
+
if (mergedConfig.preserveSystemMessage) {
|
|
1264
|
+
systemMessage = messages.find((m) => m.role === "system");
|
|
1265
|
+
nonSystemMessages = messages.filter((m) => m.role !== "system");
|
|
1266
|
+
} else nonSystemMessages = [...messages];
|
|
1267
|
+
const systemTokens = systemMessage ? estimateMessageTokens(systemMessage) : 0;
|
|
1268
|
+
const targetTokens = mergedConfig.maxTokens - systemTokens;
|
|
1269
|
+
const toKeep = [];
|
|
1270
|
+
let keepTokens = 0;
|
|
1271
|
+
let splitIndex = nonSystemMessages.length;
|
|
1272
|
+
for (let i = nonSystemMessages.length - 1; i >= 0; i--) {
|
|
1273
|
+
const message = nonSystemMessages[i];
|
|
1274
|
+
if (!message) continue;
|
|
1275
|
+
const messageTokens = estimateMessageTokens(message);
|
|
1276
|
+
const reserveForSummary = Math.floor(targetTokens * .25);
|
|
1277
|
+
if (keepTokens + messageTokens <= targetTokens - reserveForSummary) {
|
|
1278
|
+
toKeep.unshift(message);
|
|
1279
|
+
keepTokens += messageTokens;
|
|
1280
|
+
splitIndex = i;
|
|
1281
|
+
} else break;
|
|
1282
|
+
}
|
|
1283
|
+
if (splitIndex === 0) return systemMessage ? [systemMessage, ...toKeep] : toKeep;
|
|
1284
|
+
const toSummarize = nonSystemMessages.slice(0, splitIndex);
|
|
1285
|
+
const summaryMessage = await mergedConfig.summarizer(toSummarize);
|
|
1286
|
+
const result = [];
|
|
1287
|
+
if (systemMessage) result.push(systemMessage);
|
|
1288
|
+
result.push(summaryMessage);
|
|
1289
|
+
result.push(...toKeep);
|
|
1290
|
+
return result;
|
|
1291
|
+
}
|
|
1292
|
+
return {
|
|
1293
|
+
async apply(messages) {
|
|
1294
|
+
if (messages.length === 0) return messages;
|
|
1295
|
+
if (estimateTokens(messages) <= mergedConfig.maxTokens) return messages;
|
|
1296
|
+
if (mergedConfig.strategy === "summarize") return applySummarize(messages);
|
|
1297
|
+
return applyFIFO(messages);
|
|
1298
|
+
},
|
|
1299
|
+
applySync(messages) {
|
|
1300
|
+
if (messages.length === 0) return messages;
|
|
1301
|
+
if (estimateTokens(messages) <= mergedConfig.maxTokens) return messages;
|
|
1302
|
+
if (mergedConfig.strategy === "summarize") logWarn("applySync does not support summarize strategy, falling back to FIFO");
|
|
1303
|
+
return applyFIFO(messages);
|
|
1304
|
+
},
|
|
1305
|
+
estimateTokens,
|
|
1306
|
+
estimateMessageTokens
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Creates a compact window for short context models (4K tokens).
|
|
1311
|
+
*
|
|
1312
|
+
* @returns MessageWindow with 4K token limit.
|
|
1313
|
+
*
|
|
1314
|
+
* @example
|
|
1315
|
+
* ```ts
|
|
1316
|
+
* const window = createCompactWindow();
|
|
1317
|
+
* const truncated = await window.apply(messages);
|
|
1318
|
+
* ```
|
|
1319
|
+
*/
|
|
1320
|
+
function createCompactWindow() {
|
|
1321
|
+
return createMessageWindow({
|
|
1322
|
+
maxTokens: 4096,
|
|
1323
|
+
preserveSystemMessage: true,
|
|
1324
|
+
strategy: "fifo"
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* Creates a standard window for medium context models (8K tokens).
|
|
1329
|
+
*
|
|
1330
|
+
* @returns MessageWindow with 8K token limit.
|
|
1331
|
+
*
|
|
1332
|
+
* @example
|
|
1333
|
+
* ```ts
|
|
1334
|
+
* const window = createStandardWindow();
|
|
1335
|
+
* const truncated = await window.apply(messages);
|
|
1336
|
+
* ```
|
|
1337
|
+
*/
|
|
1338
|
+
function createStandardWindow() {
|
|
1339
|
+
return createMessageWindow({
|
|
1340
|
+
maxTokens: 8192,
|
|
1341
|
+
preserveSystemMessage: true,
|
|
1342
|
+
strategy: "fifo"
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Creates a large window for long context models (32K tokens).
|
|
1347
|
+
*
|
|
1348
|
+
* @returns MessageWindow with 32K token limit.
|
|
1349
|
+
*
|
|
1350
|
+
* @example
|
|
1351
|
+
* ```ts
|
|
1352
|
+
* const window = createLargeWindow();
|
|
1353
|
+
* const truncated = await window.apply(messages);
|
|
1354
|
+
* ```
|
|
1355
|
+
*/
|
|
1356
|
+
function createLargeWindow() {
|
|
1357
|
+
return createMessageWindow({
|
|
1358
|
+
maxTokens: 32768,
|
|
1359
|
+
preserveSystemMessage: true,
|
|
1360
|
+
strategy: "fifo"
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Creates an extended window for very long context models (128K tokens).
|
|
1365
|
+
*
|
|
1366
|
+
* @returns MessageWindow with 128K token limit.
|
|
1367
|
+
*
|
|
1368
|
+
* @example
|
|
1369
|
+
* ```ts
|
|
1370
|
+
* const window = createExtendedWindow();
|
|
1371
|
+
* const truncated = await window.apply(messages);
|
|
1372
|
+
* ```
|
|
1373
|
+
*/
|
|
1374
|
+
function createExtendedWindow() {
|
|
1375
|
+
return createMessageWindow({
|
|
1376
|
+
maxTokens: 131072,
|
|
1377
|
+
preserveSystemMessage: true,
|
|
1378
|
+
strategy: "fifo"
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
//#endregion
|
|
1383
|
+
//#region src/server/helpers.ts
|
|
1384
|
+
/**
|
|
1385
|
+
* @fileoverview api-helpers.ts
|
|
1386
|
+
*/
|
|
1387
|
+
/**
|
|
1388
|
+
* API Route Helpers
|
|
1389
|
+
* DRY utilities for Next.js API routes with database persistence
|
|
1390
|
+
*/
|
|
1391
|
+
const defaultWaitUntil = (promise) => {};
|
|
1392
|
+
const defaultTokenTtlSeconds = () => {
|
|
1393
|
+
const raw = process.env.STREAM_RESUME_TOKEN_TTL_SEC ?? "3600";
|
|
1394
|
+
const parsed = Number.parseInt(raw, 10);
|
|
1395
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 3600;
|
|
1396
|
+
};
|
|
1397
|
+
const getResumptionSigningKey = () => process.env.STREAM_RESUME_SIGNING_KEY ?? process.env.AUDIT_SIGNING_KEY;
|
|
1398
|
+
const encodeBase64Url = (value) => {
|
|
1399
|
+
if (typeof Buffer !== "undefined") return Buffer.from(value, "utf8").toString("base64url");
|
|
1400
|
+
if (typeof btoa !== "undefined") return btoa(value).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
1401
|
+
throw new Error("Base64 encoding unavailable");
|
|
1402
|
+
};
|
|
1403
|
+
const decodeBase64Url = (value) => {
|
|
1404
|
+
if (typeof Buffer !== "undefined") return Buffer.from(value, "base64url").toString("utf8");
|
|
1405
|
+
if (typeof atob !== "undefined") {
|
|
1406
|
+
const normalized = value.replace(/-/g, "+").replace(/_/g, "/");
|
|
1407
|
+
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
|
|
1408
|
+
return atob(padded);
|
|
1409
|
+
}
|
|
1410
|
+
throw new Error("Base64 decoding unavailable");
|
|
1411
|
+
};
|
|
1412
|
+
const createResumptionToken = async (payload) => {
|
|
1413
|
+
const signingKey = getResumptionSigningKey();
|
|
1414
|
+
if (!signingKey) throw new Error("STREAM_RESUME_SIGNING_KEY or AUDIT_SIGNING_KEY is required for resumption");
|
|
1415
|
+
const encodedPayload = encodeBase64Url(JSON.stringify(payload));
|
|
1416
|
+
return `${encodedPayload}.${await generateSignature({ payload: encodedPayload }, signingKey)}`;
|
|
1417
|
+
};
|
|
1418
|
+
const verifyResumptionToken = async (token) => {
|
|
1419
|
+
const signingKey = getResumptionSigningKey();
|
|
1420
|
+
if (!signingKey) throw new Error("STREAM_RESUME_SIGNING_KEY or AUDIT_SIGNING_KEY is required for resumption");
|
|
1421
|
+
const parts = token.split(".");
|
|
1422
|
+
if (parts.length !== 2) return null;
|
|
1423
|
+
const encodedPayload = parts[0];
|
|
1424
|
+
const signature = parts[1];
|
|
1425
|
+
if (!encodedPayload || !signature) return null;
|
|
1426
|
+
if (!await verifySignature({ payload: encodedPayload }, signature, signingKey)) return null;
|
|
1427
|
+
const payload = JSON.parse(decodeBase64Url(encodedPayload));
|
|
1428
|
+
if (!payload || typeof payload !== "object") return null;
|
|
1429
|
+
if (Date.now() > payload.expiresAt) return null;
|
|
1430
|
+
return payload;
|
|
1431
|
+
};
|
|
1432
|
+
const resolveStreamResumptionOptions = (options) => {
|
|
1433
|
+
if (typeof options === "function") return { waitUntil: options };
|
|
1434
|
+
return options ?? {};
|
|
1435
|
+
};
|
|
1436
|
+
const resolveSetupResumableStreamOptions = (options) => {
|
|
1437
|
+
if (typeof options === "function") return { waitUntil: options };
|
|
1438
|
+
return options ?? {};
|
|
1439
|
+
};
|
|
1440
|
+
const isRecord = (value) => {
|
|
1441
|
+
return typeof value === "object" && value !== null;
|
|
1442
|
+
};
|
|
1443
|
+
const normalizeError$1 = (error) => {
|
|
1444
|
+
if (error instanceof Error) return { message: error.message };
|
|
1445
|
+
if (typeof error === "string") return { message: error };
|
|
1446
|
+
if (isRecord(error) && typeof error.message === "string") return { message: error.message };
|
|
1447
|
+
return { message: "Unknown error" };
|
|
1448
|
+
};
|
|
1449
|
+
const toByteStream = (stream) => {
|
|
1450
|
+
const encoder = new TextEncoder();
|
|
1451
|
+
return stream.pipeThrough(new TransformStream({ transform(chunk, controller) {
|
|
1452
|
+
if (typeof chunk === "string") {
|
|
1453
|
+
controller.enqueue(encoder.encode(chunk));
|
|
1454
|
+
return;
|
|
1455
|
+
}
|
|
1456
|
+
if (chunk instanceof Uint8Array) {
|
|
1457
|
+
controller.enqueue(chunk);
|
|
1458
|
+
return;
|
|
1459
|
+
}
|
|
1460
|
+
controller.enqueue(encoder.encode(String(chunk)));
|
|
1461
|
+
} }));
|
|
1462
|
+
};
|
|
1463
|
+
/**
|
|
1464
|
+
* Extracts error message from unknown error type.
|
|
1465
|
+
* Provides consistent error message extraction across all routes.
|
|
1466
|
+
*
|
|
1467
|
+
* @param {unknown} error - The error to extract message from
|
|
1468
|
+
* @returns {string} The error message
|
|
1469
|
+
*
|
|
1470
|
+
* @example
|
|
1471
|
+
* ```ts
|
|
1472
|
+
* // Extract error message
|
|
1473
|
+
* const message = getErrorMessage(error);
|
|
1474
|
+
* return jsonError(message, 500);
|
|
1475
|
+
* ```
|
|
1476
|
+
*/
|
|
1477
|
+
const getErrorMessage = (error) => {
|
|
1478
|
+
return normalizeError$1(error).message;
|
|
1479
|
+
};
|
|
1480
|
+
/**
|
|
1481
|
+
* Creates a JSON error response with consistent format.
|
|
1482
|
+
*
|
|
1483
|
+
* @param {string | Error} error - The error message or Error object
|
|
1484
|
+
* @param {number} [status] - HTTP status code
|
|
1485
|
+
* @returns {Response} JSON error response
|
|
1486
|
+
*
|
|
1487
|
+
* @example
|
|
1488
|
+
* ```ts
|
|
1489
|
+
* // Create error response
|
|
1490
|
+
* return jsonError('Invalid request', 400);
|
|
1491
|
+
* // or
|
|
1492
|
+
* return jsonError(error, 500);
|
|
1493
|
+
* ```
|
|
1494
|
+
*/
|
|
1495
|
+
const jsonError = (error, status = 500) => {
|
|
1496
|
+
const message = typeof error === "string" ? error : error.message;
|
|
1497
|
+
return new Response(JSON.stringify({ error: message }), {
|
|
1498
|
+
status,
|
|
1499
|
+
headers: { "Content-Type": "application/json" }
|
|
1500
|
+
});
|
|
1501
|
+
};
|
|
1502
|
+
/**
|
|
1503
|
+
* Creates a JSON success response.
|
|
1504
|
+
*
|
|
1505
|
+
* @template T
|
|
1506
|
+
* @param {T} data - The data to return
|
|
1507
|
+
* @param {number} [status] - HTTP status code
|
|
1508
|
+
* @returns {Response} JSON success response
|
|
1509
|
+
*
|
|
1510
|
+
* @example
|
|
1511
|
+
* ```ts
|
|
1512
|
+
* // Create success response
|
|
1513
|
+
* return jsonSuccess({ userId: '123', name: 'John' }, 200);
|
|
1514
|
+
* ```
|
|
1515
|
+
*/
|
|
1516
|
+
const jsonSuccess = (data, status = 200) => {
|
|
1517
|
+
return new Response(JSON.stringify(data), {
|
|
1518
|
+
status,
|
|
1519
|
+
headers: { "Content-Type": "application/json" }
|
|
1520
|
+
});
|
|
1521
|
+
};
|
|
1522
|
+
/**
|
|
1523
|
+
* Parses and validates request body.
|
|
1524
|
+
* Returns typed data or error response.
|
|
1525
|
+
*
|
|
1526
|
+
* @template T
|
|
1527
|
+
* @param {Request} req - The request object
|
|
1528
|
+
* @returns {Promise<{ data: T } | { error: Response }>} Parsed data or error response
|
|
1529
|
+
*
|
|
1530
|
+
* @example
|
|
1531
|
+
* ```ts
|
|
1532
|
+
* // Parse request body
|
|
1533
|
+
* const result = await parseRequestBody<{ message: string }>(req);
|
|
1534
|
+
* if ('error' in result) {
|
|
1535
|
+
* return result.error;
|
|
1536
|
+
* }
|
|
1537
|
+
* const { message } = result.data;
|
|
1538
|
+
* ```
|
|
1539
|
+
*/
|
|
1540
|
+
const parseRequestBody = async (req) => {
|
|
1541
|
+
try {
|
|
1542
|
+
return { data: await req.json() };
|
|
1543
|
+
} catch {
|
|
1544
|
+
return { error: jsonError("Invalid JSON in request body", 400) };
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
/**
|
|
1548
|
+
* Validates that messages is a non-empty array of UIMessage.
|
|
1549
|
+
*
|
|
1550
|
+
* @param {unknown} messages - The value to validate
|
|
1551
|
+
* @returns {messages is UIMessage[]} True if messages is a valid array
|
|
1552
|
+
*
|
|
1553
|
+
* @example
|
|
1554
|
+
* ```ts
|
|
1555
|
+
* // Validate messages
|
|
1556
|
+
* if (!validateMessages(messages)) {
|
|
1557
|
+
* return jsonError('Invalid messages format', 400);
|
|
1558
|
+
* }
|
|
1559
|
+
* ```
|
|
1560
|
+
*/
|
|
1561
|
+
const validateMessages = (messages) => {
|
|
1562
|
+
return Array.isArray(messages) && messages.length > 0;
|
|
1563
|
+
};
|
|
1564
|
+
/**
|
|
1565
|
+
* Common response options for streaming
|
|
1566
|
+
*/
|
|
1567
|
+
const STREAM_RESPONSE_OPTIONS = {
|
|
1568
|
+
standard: {
|
|
1569
|
+
sendSources: true,
|
|
1570
|
+
sendReasoning: true
|
|
1571
|
+
},
|
|
1572
|
+
sources: { sendSources: true },
|
|
1573
|
+
minimal: {}
|
|
1574
|
+
};
|
|
1575
|
+
/**
|
|
1576
|
+
* Creates experimental_telemetry config for AI SDK v6.
|
|
1577
|
+
* Provides observability for AI operations.
|
|
1578
|
+
*
|
|
1579
|
+
* @param {object} options - Telemetry configuration options
|
|
1580
|
+
* @param {string} options.functionId - Unique identifier for the function
|
|
1581
|
+
* @param {string} options.component - Component name
|
|
1582
|
+
* @param {string} [options.version] - Version string
|
|
1583
|
+
* @param {Record<string, unknown>} [options.metadata] - Additional metadata
|
|
1584
|
+
* @returns {object} Telemetry configuration object
|
|
1585
|
+
*
|
|
1586
|
+
* @example
|
|
1587
|
+
* ```ts
|
|
1588
|
+
* // Create telemetry config
|
|
1589
|
+
* const telemetry = createTelemetryConfig({
|
|
1590
|
+
* functionId: 'chat-handler',
|
|
1591
|
+
* component: 'chat-api',
|
|
1592
|
+
* version: '1.0.0',
|
|
1593
|
+
* metadata: { userId: '123' }
|
|
1594
|
+
* });
|
|
1595
|
+
* ```
|
|
1596
|
+
*/
|
|
1597
|
+
const createTelemetryConfig = (options) => {
|
|
1598
|
+
return {
|
|
1599
|
+
isEnabled: true,
|
|
1600
|
+
functionId: options.functionId,
|
|
1601
|
+
metadata: {
|
|
1602
|
+
component: options.component,
|
|
1603
|
+
version: options.version || "1.0.0",
|
|
1604
|
+
...options.metadata
|
|
1605
|
+
}
|
|
1606
|
+
};
|
|
1607
|
+
};
|
|
1608
|
+
/**
|
|
1609
|
+
* Handle stream resumption for GET /stream routes
|
|
1610
|
+
* Queries the database for activeStreamId and resumes if found.
|
|
1611
|
+
*
|
|
1612
|
+
* Note: Requires Redis to be configured for resumable streams.
|
|
1613
|
+
*/
|
|
1614
|
+
async function handleStreamResumption(req, options) {
|
|
1615
|
+
const { waitUntil, auth } = resolveStreamResumptionOptions(options);
|
|
1616
|
+
const effectiveWaitUntil = waitUntil ?? defaultWaitUntil;
|
|
1617
|
+
const chatId = req.headers.get("X-Chat-ID");
|
|
1618
|
+
if (!chatId) return jsonError("Missing X-Chat-ID header", 400);
|
|
1619
|
+
if (!auth) return jsonError("Unauthorized", 401);
|
|
1620
|
+
const userContext = await auth.getUserContext(req);
|
|
1621
|
+
if (!userContext) return jsonError("Unauthorized", 401);
|
|
1622
|
+
if (auth.verifyOwnership) {
|
|
1623
|
+
if (!await auth.verifyOwnership(userContext.userId, chatId)) return jsonError("Forbidden", 403);
|
|
1624
|
+
}
|
|
1625
|
+
const resumptionToken = (await findAIConversationOrm({ id: chatId }))?.activeStreamId;
|
|
1626
|
+
if (!resumptionToken) return new Response(null, { status: 204 });
|
|
1627
|
+
try {
|
|
1628
|
+
const payload = await verifyResumptionToken(resumptionToken);
|
|
1629
|
+
if (payload?.chatId !== chatId || payload.userId !== userContext.userId) {
|
|
1630
|
+
await updateAIConversationOrm({ id: chatId }, { activeStreamId: null }).catch(() => {});
|
|
1631
|
+
return new Response(null, { status: 204 });
|
|
1632
|
+
}
|
|
1633
|
+
const stream = await createResumableStream({ waitUntil: effectiveWaitUntil }).resumeExistingStream(payload.streamId);
|
|
1634
|
+
if (!stream) {
|
|
1635
|
+
await updateAIConversationOrm({ id: chatId }, { activeStreamId: null }).catch(() => {});
|
|
1636
|
+
return new Response(null, { status: 204 });
|
|
1637
|
+
}
|
|
1638
|
+
const responseStream = toByteStream(stream);
|
|
1639
|
+
return new Response(responseStream, { headers: {
|
|
1640
|
+
...UI_MESSAGE_STREAM_HEADERS,
|
|
1641
|
+
"X-Stream-Resumed": "true"
|
|
1642
|
+
} });
|
|
1643
|
+
} catch (error) {
|
|
1644
|
+
logWarn("[handleStreamResumption] Failed to resume stream:", {
|
|
1645
|
+
error,
|
|
1646
|
+
chatId
|
|
1647
|
+
});
|
|
1648
|
+
return new Response(null, { status: 204 });
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
/**
|
|
1652
|
+
* Sets up resumable stream for agent responses.
|
|
1653
|
+
* Wraps a response stream with resumption capabilities and stores
|
|
1654
|
+
* the stream ID in the database for client-side resume.
|
|
1655
|
+
*
|
|
1656
|
+
* Note: Fails gracefully if Redis is not available - stream will still work,
|
|
1657
|
+
* just without resumption support across page refreshes.
|
|
1658
|
+
*
|
|
1659
|
+
* @param {Response} response - The response stream to wrap
|
|
1660
|
+
* @param {string} chatId - The chat/conversation ID for stream tracking
|
|
1661
|
+
* @returns {Promise<void>}
|
|
1662
|
+
*
|
|
1663
|
+
* @example
|
|
1664
|
+
* ```ts
|
|
1665
|
+
* // Setup resumable stream
|
|
1666
|
+
* const response = streamText(...);
|
|
1667
|
+
* await setupResumableStream(response, chatId);
|
|
1668
|
+
* return response;
|
|
1669
|
+
* ```
|
|
1670
|
+
*/
|
|
1671
|
+
const setupResumableStream = async (response, chatId, options) => {
|
|
1672
|
+
const { waitUntil, userId, tokenTtlSeconds } = resolveSetupResumableStreamOptions(options);
|
|
1673
|
+
const effectiveWaitUntil = waitUntil ?? defaultWaitUntil;
|
|
1674
|
+
try {
|
|
1675
|
+
const streamId = generateId();
|
|
1676
|
+
const streamContext = createResumableStream({ waitUntil: effectiveWaitUntil });
|
|
1677
|
+
if (response.body) {
|
|
1678
|
+
await streamContext.createNewResumableStream(streamId, () => response.body);
|
|
1679
|
+
if (!userId) {
|
|
1680
|
+
logWarn("[setupResumableStream] Missing userId, resumption disabled", { chatId });
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
const ttlSeconds = tokenTtlSeconds ?? defaultTokenTtlSeconds();
|
|
1684
|
+
const resumptionToken = await createResumptionToken({
|
|
1685
|
+
streamId,
|
|
1686
|
+
chatId,
|
|
1687
|
+
userId,
|
|
1688
|
+
issuedAt: Date.now(),
|
|
1689
|
+
expiresAt: Date.now() + ttlSeconds * 1e3
|
|
1690
|
+
});
|
|
1691
|
+
await updateAIConversationOrm({ id: chatId }, { activeStreamId: resumptionToken });
|
|
1692
|
+
}
|
|
1693
|
+
} catch (error) {
|
|
1694
|
+
if (error instanceof Error && "code" in error && error.code === "ECONNREFUSED") return;
|
|
1695
|
+
logWarn("[setupResumableStream] Failed to setup resumable stream:", { error });
|
|
1696
|
+
}
|
|
1697
|
+
};
|
|
1698
|
+
/**
|
|
1699
|
+
* Gets or generates a chat ID for non-persistent sessions.
|
|
1700
|
+
*
|
|
1701
|
+
* Note: For persistent sessions with database storage, use initializePersistentSession instead.
|
|
1702
|
+
*
|
|
1703
|
+
* @param {string} [id] - Optional existing chat ID
|
|
1704
|
+
* @returns {string} The chat ID
|
|
1705
|
+
*
|
|
1706
|
+
* @example
|
|
1707
|
+
* ```ts
|
|
1708
|
+
* // Initialize chat session
|
|
1709
|
+
* const chatId = initializeChatSession();
|
|
1710
|
+
* // or with existing ID
|
|
1711
|
+
* const chatId = initializeChatSession('existing-id');
|
|
1712
|
+
* ```
|
|
1713
|
+
*/
|
|
1714
|
+
const initializeChatSession = (id) => {
|
|
1715
|
+
return id ?? generateId();
|
|
1716
|
+
};
|
|
1717
|
+
/**
|
|
1718
|
+
* Initializes a persistent chat session with database storage.
|
|
1719
|
+
* Use this instead of initializeChatSession for demos that need persistence.
|
|
1720
|
+
*
|
|
1721
|
+
* @param {object} [options] - Session initialization options
|
|
1722
|
+
* @param {string} [options.sessionId] - Existing session ID
|
|
1723
|
+
* @param {string} [options.userId] - User ID for the session
|
|
1724
|
+
* @param {string} [options.model] - Model name for the session
|
|
1725
|
+
* @param {string} [options.demo] - Demo identifier
|
|
1726
|
+
* @returns {Promise<{ sessionId: string; isNew: boolean }>} Session ID and whether it's new
|
|
1727
|
+
*
|
|
1728
|
+
* @example
|
|
1729
|
+
* ```ts
|
|
1730
|
+
* // Initialize persistent session
|
|
1731
|
+
* const { sessionId, isNew } = await initializePersistentSession({
|
|
1732
|
+
* userId: 'user-123',
|
|
1733
|
+
* model: 'gpt-4'
|
|
1734
|
+
* });
|
|
1735
|
+
* ```
|
|
1736
|
+
*/
|
|
1737
|
+
const initializePersistentSession = async (options) => {
|
|
1738
|
+
let session;
|
|
1739
|
+
let isNew = false;
|
|
1740
|
+
if (options?.sessionId) session = await findAIConversationOrm({ id: options.sessionId });
|
|
1741
|
+
if (!session) {
|
|
1742
|
+
session = await createAIConversationOrm({
|
|
1743
|
+
...options?.userId ? { user: { connect: { id: options.userId } } } : {},
|
|
1744
|
+
primaryModel: options?.model,
|
|
1745
|
+
sessionType: AISessionType.CONVERSATION
|
|
1746
|
+
});
|
|
1747
|
+
isNew = true;
|
|
1748
|
+
} else if (session.activeStreamId) await updateAIConversationOrm({ id: session.id }, { activeStreamId: null }).catch(() => {});
|
|
1749
|
+
return {
|
|
1750
|
+
sessionId: session.id,
|
|
1751
|
+
isNew
|
|
1752
|
+
};
|
|
1753
|
+
};
|
|
1754
|
+
/**
|
|
1755
|
+
* Wrapper for initializePersistentSession that matches InitializeSessionFn signature
|
|
1756
|
+
* Use this with createAgentRouteHandler for consistent session initialization
|
|
1757
|
+
*/
|
|
1758
|
+
const initializePersistentSessionFn = async (sessionId) => {
|
|
1759
|
+
logInfo("[initializePersistentSessionFn] Starting with sessionId:", { sessionId });
|
|
1760
|
+
try {
|
|
1761
|
+
const result = await initializePersistentSession({ sessionId });
|
|
1762
|
+
logInfo("[initializePersistentSessionFn] Success:", { result });
|
|
1763
|
+
return result;
|
|
1764
|
+
} catch (error) {
|
|
1765
|
+
logError(error instanceof Error ? error : new Error(String(error)), { message: "[initializePersistentSessionFn] Error:" });
|
|
1766
|
+
throw error;
|
|
1767
|
+
}
|
|
1768
|
+
};
|
|
1769
|
+
/**
|
|
1770
|
+
* Save a user message to the database
|
|
1771
|
+
*/
|
|
1772
|
+
async function persistUserMessage(sessionId, content, options) {
|
|
1773
|
+
await createAIMessageOrm({
|
|
1774
|
+
conversation: { connect: { id: sessionId } },
|
|
1775
|
+
role: AIMessageRole.USER,
|
|
1776
|
+
content
|
|
1777
|
+
});
|
|
1778
|
+
if (options?.autoTitle) {
|
|
1779
|
+
const title = content.slice(0, 50) + (content.length > 50 ? "..." : "");
|
|
1780
|
+
await updateAIConversationOrm({ id: sessionId }, { title }).catch(() => {});
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Saves an assistant message to the database.
|
|
1785
|
+
*
|
|
1786
|
+
* @param {string} sessionId - The session ID
|
|
1787
|
+
* @param {string} content - The message content
|
|
1788
|
+
* @returns {Promise<void>}
|
|
1789
|
+
*
|
|
1790
|
+
* @example
|
|
1791
|
+
* ```ts
|
|
1792
|
+
* // Persist assistant message
|
|
1793
|
+
* await persistAssistantMessage(sessionId, 'Hello! How can I help you?');
|
|
1794
|
+
* ```
|
|
1795
|
+
*/
|
|
1796
|
+
const persistAssistantMessage = async (sessionId, content) => {
|
|
1797
|
+
await createAIMessageOrm({
|
|
1798
|
+
conversation: { connect: { id: sessionId } },
|
|
1799
|
+
role: AIMessageRole.ASSISTANT,
|
|
1800
|
+
content
|
|
1801
|
+
});
|
|
1802
|
+
};
|
|
1803
|
+
/**
|
|
1804
|
+
* Creates a standard onFinish callback for AI SDK streams.
|
|
1805
|
+
* Combines stream cleanup with optional usage logging and persistence.
|
|
1806
|
+
*
|
|
1807
|
+
* AI SDK v6 native feature - leverages built-in onFinish callback
|
|
1808
|
+
* instead of manually handling result.usage Promise.
|
|
1809
|
+
*
|
|
1810
|
+
* @param {string} chatId - The chat ID for stream tracking
|
|
1811
|
+
* @param {object} [options] - Callback options
|
|
1812
|
+
* @param {boolean} [options.logUsage] - Whether to log usage statistics
|
|
1813
|
+
* @param {string} [options.demo] - Demo identifier for logging
|
|
1814
|
+
* @param {Logger} [options.logger] - Logger instance for usage logging
|
|
1815
|
+
* @param {string} [options.persistSessionId] - Session ID for database persistence
|
|
1816
|
+
* @returns {(event: StreamFinishEvent) => Promise<void>} onFinish callback function
|
|
1817
|
+
*
|
|
1818
|
+
* @example
|
|
1819
|
+
* ```ts
|
|
1820
|
+
* // Create onFinish callback
|
|
1821
|
+
* const onFinish = createOnFinishCallback(chatId, {
|
|
1822
|
+
* logUsage: true,
|
|
1823
|
+
* demo: 'chat-demo',
|
|
1824
|
+
* persistSessionId: sessionId
|
|
1825
|
+
* });
|
|
1826
|
+
* ```
|
|
1827
|
+
*/
|
|
1828
|
+
const createOnFinishCallback = (chatId, options) => {
|
|
1829
|
+
return async (event) => {
|
|
1830
|
+
await updateAIConversationOrm({ id: chatId }, { activeStreamId: null }).catch(() => {});
|
|
1831
|
+
if (options?.persistSessionId && event?.text) await persistAssistantMessage(options.persistSessionId, event.text).catch((err) => {
|
|
1832
|
+
options?.logger?.info?.("Failed to persist assistant message", { error: String(err) });
|
|
1833
|
+
});
|
|
1834
|
+
if (options?.logUsage && options?.logger && event?.usage) options.logger.info(`${options.demo || "stream"} usage`, {
|
|
1835
|
+
...event.usage,
|
|
1836
|
+
totalUsage: event.totalUsage,
|
|
1837
|
+
demo: options.demo
|
|
1838
|
+
});
|
|
1839
|
+
};
|
|
1840
|
+
};
|
|
1841
|
+
/**
|
|
1842
|
+
* Calculate model cost with extended pricing data including Vercel AI Gateway models.
|
|
1843
|
+
*
|
|
1844
|
+
* Supports provider-prefixed model IDs (e.g., 'openai/gpt-4o', 'anthropic/claude-sonnet-4-5')
|
|
1845
|
+
* and includes pricing for newer models and gateway-specific models.
|
|
1846
|
+
*
|
|
1847
|
+
* @param modelId - Model identifier (e.g., 'openai/gpt-4o', 'anthropic/claude-sonnet-4-5')
|
|
1848
|
+
* @param usage - Token usage object
|
|
1849
|
+
* @returns Estimated cost in USD
|
|
1850
|
+
*
|
|
1851
|
+
* @example
|
|
1852
|
+
* ```typescript
|
|
1853
|
+
* const cost = calculateModelCost('openai/gpt-4o', {
|
|
1854
|
+
* inputTokens: 1000,
|
|
1855
|
+
* outputTokens: 500
|
|
1856
|
+
* });
|
|
1857
|
+
* // Returns: 0.005 (estimated cost in USD)
|
|
1858
|
+
* ```
|
|
1859
|
+
*/
|
|
1860
|
+
const calculateModelCost = (modelId, usage) => {
|
|
1861
|
+
const MODEL_COSTS = {
|
|
1862
|
+
"openai/gpt-4o": {
|
|
1863
|
+
input: .0025,
|
|
1864
|
+
output: .01
|
|
1865
|
+
},
|
|
1866
|
+
"openai/gpt-4o-mini": {
|
|
1867
|
+
input: 15e-5,
|
|
1868
|
+
output: 6e-4
|
|
1869
|
+
},
|
|
1870
|
+
"openai/gpt-5.1-instant": {
|
|
1871
|
+
input: 1e-4,
|
|
1872
|
+
output: 4e-4
|
|
1873
|
+
},
|
|
1874
|
+
"anthropic/claude-sonnet-4-20250514": {
|
|
1875
|
+
input: .003,
|
|
1876
|
+
output: .015
|
|
1877
|
+
},
|
|
1878
|
+
"anthropic/claude-opus-4-5-20251101": {
|
|
1879
|
+
input: .015,
|
|
1880
|
+
output: .075
|
|
1881
|
+
},
|
|
1882
|
+
"perplexity/sonar": {
|
|
1883
|
+
input: .001,
|
|
1884
|
+
output: .001
|
|
1885
|
+
},
|
|
1886
|
+
"gpt-4o": {
|
|
1887
|
+
input: .0025,
|
|
1888
|
+
output: .01
|
|
1889
|
+
},
|
|
1890
|
+
"gpt-4o-mini": {
|
|
1891
|
+
input: 15e-5,
|
|
1892
|
+
output: 6e-4
|
|
1893
|
+
},
|
|
1894
|
+
"gpt-5.1-instant": {
|
|
1895
|
+
input: 1e-4,
|
|
1896
|
+
output: 4e-4
|
|
1897
|
+
},
|
|
1898
|
+
"claude-sonnet-4": {
|
|
1899
|
+
input: .003,
|
|
1900
|
+
output: .015
|
|
1901
|
+
},
|
|
1902
|
+
"claude-opus-4": {
|
|
1903
|
+
input: .015,
|
|
1904
|
+
output: .075
|
|
1905
|
+
},
|
|
1906
|
+
default: {
|
|
1907
|
+
input: .001,
|
|
1908
|
+
output: .002
|
|
1909
|
+
}
|
|
1910
|
+
};
|
|
1911
|
+
const pricing = MODEL_COSTS[modelId] ?? MODEL_COSTS.default ?? {
|
|
1912
|
+
input: .001,
|
|
1913
|
+
output: .002
|
|
1914
|
+
};
|
|
1915
|
+
const inputTokens = usage.inputTokens ?? 0;
|
|
1916
|
+
const outputTokens = usage.outputTokens ?? 0;
|
|
1917
|
+
return inputTokens / 1e3 * pricing.input + outputTokens / 1e3 * pricing.output;
|
|
1918
|
+
};
|
|
1919
|
+
/**
|
|
1920
|
+
* Estimate input tokens from text.
|
|
1921
|
+
*
|
|
1922
|
+
* Uses rough approximation: ~4 characters per token for English text.
|
|
1923
|
+
* This is a simple heuristic and may vary by language and content type.
|
|
1924
|
+
*
|
|
1925
|
+
* @param text - Text to estimate tokens for
|
|
1926
|
+
* @returns Estimated number of input tokens
|
|
1927
|
+
*
|
|
1928
|
+
* @example
|
|
1929
|
+
* ```typescript
|
|
1930
|
+
* estimateInputTokens('Hello world'); // Returns: 3 (12 chars / 4)
|
|
1931
|
+
* ```
|
|
1932
|
+
*/
|
|
1933
|
+
const estimateInputTokens = (text) => Math.ceil(text.length / 4);
|
|
1934
|
+
/**
|
|
1935
|
+
* Extract model name from provider-prefixed model ID.
|
|
1936
|
+
*
|
|
1937
|
+
* Removes provider prefix from model IDs like "openai/gpt-4o" or "anthropic/claude-3-5-sonnet".
|
|
1938
|
+
* Handles both "provider/model" and "provider:model" formats.
|
|
1939
|
+
*
|
|
1940
|
+
* @param modelId - Model identifier (e.g., "openai/gpt-4o", "openai:gpt-4o", "gpt-4o")
|
|
1941
|
+
* @returns Model name without provider prefix
|
|
1942
|
+
*
|
|
1943
|
+
* @example
|
|
1944
|
+
* ```typescript
|
|
1945
|
+
* extractModelName('openai/gpt-4o'); // Returns: 'gpt-4o'
|
|
1946
|
+
* extractModelName('openai:gpt-4o'); // Returns: 'gpt-4o'
|
|
1947
|
+
* extractModelName('gpt-4o'); // Returns: 'gpt-4o'
|
|
1948
|
+
* ```
|
|
1949
|
+
*/
|
|
1950
|
+
const extractModelName = (modelId) => {
|
|
1951
|
+
if (modelId.includes("/")) {
|
|
1952
|
+
const parts = modelId.split("/");
|
|
1953
|
+
return parts[parts.length - 1] ?? modelId;
|
|
1954
|
+
}
|
|
1955
|
+
if (modelId.includes(":")) {
|
|
1956
|
+
const parts = modelId.split(":");
|
|
1957
|
+
return parts[parts.length - 1] ?? modelId;
|
|
1958
|
+
}
|
|
1959
|
+
return modelId;
|
|
1960
|
+
};
|
|
1961
|
+
/**
|
|
1962
|
+
* Round cost to specified decimal places.
|
|
1963
|
+
*
|
|
1964
|
+
* @param cost - Cost value to round
|
|
1965
|
+
* @param decimals - Number of decimal places (default: 6)
|
|
1966
|
+
* @returns Rounded cost value
|
|
1967
|
+
*
|
|
1968
|
+
* @example
|
|
1969
|
+
* ```typescript
|
|
1970
|
+
* roundCost(0.123456789); // Returns: 0.123457
|
|
1971
|
+
* roundCost(0.123456789, 2); // Returns: 0.12
|
|
1972
|
+
* ```
|
|
1973
|
+
*/
|
|
1974
|
+
const roundCost = (cost, decimals = 6) => Math.round(cost * Math.pow(10, decimals)) / Math.pow(10, decimals);
|
|
1975
|
+
/**
|
|
1976
|
+
* Truncate text to specified length with ellipsis.
|
|
1977
|
+
*
|
|
1978
|
+
* @param text - Text to truncate
|
|
1979
|
+
* @param maxLength - Maximum length before truncation
|
|
1980
|
+
* @param ellipsis - Ellipsis string (default: '...')
|
|
1981
|
+
* @returns Truncated text
|
|
1982
|
+
*
|
|
1983
|
+
* @example
|
|
1984
|
+
* ```typescript
|
|
1985
|
+
* truncateText('Hello world', 8); // Returns: 'Hello...'
|
|
1986
|
+
* truncateText('Short', 10); // Returns: 'Short'
|
|
1987
|
+
* ```
|
|
1988
|
+
*/
|
|
1989
|
+
const truncateText = (text, maxLength, ellipsis = "...") => {
|
|
1990
|
+
if (text.length <= maxLength) return text;
|
|
1991
|
+
return `${text.slice(0, maxLength - ellipsis.length)}${ellipsis}`;
|
|
1992
|
+
};
|
|
1993
|
+
/**
|
|
1994
|
+
* Calculate elapsed duration in milliseconds from a start timestamp.
|
|
1995
|
+
*
|
|
1996
|
+
* @param startTime - Epoch milliseconds captured at the start of an operation
|
|
1997
|
+
* @returns Milliseconds elapsed since `startTime`
|
|
1998
|
+
*
|
|
1999
|
+
* @example
|
|
2000
|
+
* ```typescript
|
|
2001
|
+
* const startedAt = Date.now();
|
|
2002
|
+
* // ... work ...
|
|
2003
|
+
* const durationMs = calculateDuration(startedAt);
|
|
2004
|
+
* ```
|
|
2005
|
+
*/
|
|
2006
|
+
const calculateDuration = (startTime) => Date.now() - startTime;
|
|
2007
|
+
/**
|
|
2008
|
+
* Extract telemetry data from an AI SDK stream result.
|
|
2009
|
+
* Awaits text, usage, and reasoningText in parallel for efficiency.
|
|
2010
|
+
*
|
|
2011
|
+
* @param result - AI SDK stream result to extract from
|
|
2012
|
+
* @returns Parsed text, usage, and optional reasoning text
|
|
2013
|
+
*
|
|
2014
|
+
* @example
|
|
2015
|
+
* ```typescript
|
|
2016
|
+
* const telemetry = await extractTelemetry(streamResult);
|
|
2017
|
+
* console.log(telemetry.usage.totalTokens);
|
|
2018
|
+
* ```
|
|
2019
|
+
*/
|
|
2020
|
+
const extractTelemetry = async (result) => {
|
|
2021
|
+
const [text, usage, reasoningText] = await Promise.all([
|
|
2022
|
+
result.text,
|
|
2023
|
+
result.usage,
|
|
2024
|
+
result.reasoningText
|
|
2025
|
+
]);
|
|
2026
|
+
return {
|
|
2027
|
+
text,
|
|
2028
|
+
usage,
|
|
2029
|
+
reasoningText
|
|
2030
|
+
};
|
|
2031
|
+
};
|
|
2032
|
+
/**
|
|
2033
|
+
* Aggregate token usage from multiple messages or operations.
|
|
2034
|
+
*
|
|
2035
|
+
* Combines usage statistics from multiple sources, handling undefined/null values safely.
|
|
2036
|
+
* Useful for tracking cumulative usage across a conversation or session.
|
|
2037
|
+
*
|
|
2038
|
+
* @param current - Current usage statistics
|
|
2039
|
+
* @param addition - Usage to add to current
|
|
2040
|
+
* @returns Aggregated usage statistics
|
|
2041
|
+
*
|
|
2042
|
+
* @example
|
|
2043
|
+
* ```typescript
|
|
2044
|
+
* const totalUsage = aggregateUsage(
|
|
2045
|
+
* { inputTokens: 100, outputTokens: 50, totalTokens: 150 },
|
|
2046
|
+
* { inputTokens: 200, outputTokens: 100, totalTokens: 300 }
|
|
2047
|
+
* );
|
|
2048
|
+
* // Returns: { inputTokens: 300, outputTokens: 150, totalTokens: 450, ... }
|
|
2049
|
+
* ```
|
|
2050
|
+
*/
|
|
2051
|
+
const aggregateUsage = (current, addition) => {
|
|
2052
|
+
const reasoningTokens = (current.outputTokenDetails?.reasoningTokens ?? current.reasoningTokens ?? 0) + (addition.outputTokenDetails?.reasoningTokens ?? addition.reasoningTokens ?? 0);
|
|
2053
|
+
const cacheReadTokens = (current.inputTokenDetails?.cacheReadTokens ?? current.cachedInputTokens ?? 0) + (addition.inputTokenDetails?.cacheReadTokens ?? addition.cachedInputTokens ?? 0);
|
|
2054
|
+
const noCacheTokens = (current.inputTokenDetails?.noCacheTokens ?? 0) + (addition.inputTokenDetails?.noCacheTokens ?? 0);
|
|
2055
|
+
const cacheWriteTokens = (current.inputTokenDetails?.cacheWriteTokens ?? 0) + (addition.inputTokenDetails?.cacheWriteTokens ?? 0);
|
|
2056
|
+
const textTokens = (current.outputTokenDetails?.textTokens ?? 0) + (addition.outputTokenDetails?.textTokens ?? 0);
|
|
2057
|
+
return {
|
|
2058
|
+
inputTokens: (current.inputTokens ?? 0) + (addition.inputTokens ?? 0),
|
|
2059
|
+
outputTokens: (current.outputTokens ?? 0) + (addition.outputTokens ?? 0),
|
|
2060
|
+
totalTokens: (current.totalTokens ?? 0) + (addition.totalTokens ?? 0),
|
|
2061
|
+
reasoningTokens,
|
|
2062
|
+
cachedInputTokens: cacheReadTokens,
|
|
2063
|
+
inputTokenDetails: {
|
|
2064
|
+
noCacheTokens: noCacheTokens || void 0,
|
|
2065
|
+
cacheReadTokens: cacheReadTokens || void 0,
|
|
2066
|
+
cacheWriteTokens: cacheWriteTokens || void 0
|
|
2067
|
+
},
|
|
2068
|
+
outputTokenDetails: {
|
|
2069
|
+
textTokens: textTokens || void 0,
|
|
2070
|
+
reasoningTokens: reasoningTokens || void 0
|
|
2071
|
+
}
|
|
2072
|
+
};
|
|
2073
|
+
};
|
|
2074
|
+
|
|
2075
|
+
//#endregion
|
|
2076
|
+
//#region src/server/error-handler.ts
|
|
2077
|
+
/**
|
|
2078
|
+
* @fileoverview error-handler.ts
|
|
2079
|
+
*/
|
|
2080
|
+
/** Default notifier — logs to the structured logger. */
|
|
2081
|
+
const defaultNotifier = createLogNotifier();
|
|
2082
|
+
/**
|
|
2083
|
+
* Type guard to check if an error is a rate limit response.
|
|
2084
|
+
*
|
|
2085
|
+
* @param {unknown} error - The error to check
|
|
2086
|
+
* @returns {error is RateLimitResponse} True if the error is a rate limit response
|
|
2087
|
+
*
|
|
2088
|
+
* @example
|
|
2089
|
+
* ```ts
|
|
2090
|
+
* // Check if error is a rate limit error
|
|
2091
|
+
* if (isRateLimitError(error)) {
|
|
2092
|
+
* handleRateLimitError(error);
|
|
2093
|
+
* }
|
|
2094
|
+
* ```
|
|
2095
|
+
*/
|
|
2096
|
+
const isRateLimitError = (error) => {
|
|
2097
|
+
if (error && typeof error === "object" && error !== null && "error" in error && "retryAfter" in error) {
|
|
2098
|
+
const err = error;
|
|
2099
|
+
return typeof err.error === "string" && typeof err.retryAfter === "number" && err.error.toLowerCase().includes("rate limit");
|
|
2100
|
+
}
|
|
2101
|
+
return false;
|
|
2102
|
+
};
|
|
2103
|
+
/**
|
|
2104
|
+
* Handles rate limit errors by notifying the user/operator.
|
|
2105
|
+
*
|
|
2106
|
+
* @param {RateLimitError} error - The rate limit error to handle
|
|
2107
|
+
* @param {Notifier} [notifier] - Notifier to use (defaults to log-based notifier)
|
|
2108
|
+
* @returns {void}
|
|
2109
|
+
*
|
|
2110
|
+
* @example
|
|
2111
|
+
* ```ts
|
|
2112
|
+
* // Handle rate limit error
|
|
2113
|
+
* if (isRateLimitError(error)) {
|
|
2114
|
+
* handleRateLimitError(error);
|
|
2115
|
+
* }
|
|
2116
|
+
* ```
|
|
2117
|
+
*/
|
|
2118
|
+
const handleRateLimitError = (error, notifier) => {
|
|
2119
|
+
const n = notifier ?? defaultNotifier;
|
|
2120
|
+
const retryAfterMinutes = Math.ceil(error.retryAfter / 60);
|
|
2121
|
+
const retryAfterHours = Math.ceil(retryAfterMinutes / 60);
|
|
2122
|
+
let retryMessage = "";
|
|
2123
|
+
if (retryAfterHours >= 1) retryMessage = `Please try again in ${retryAfterHours} hour${retryAfterHours > 1 ? "s" : ""}`;
|
|
2124
|
+
else retryMessage = `Please try again in ${retryAfterMinutes} minute${retryAfterMinutes > 1 ? "s" : ""}`;
|
|
2125
|
+
n.notify({
|
|
2126
|
+
level: "error",
|
|
2127
|
+
message: `Rate limit exceeded. You've reached the daily limit of 10 requests. ${retryMessage}`,
|
|
2128
|
+
id: "rate-limit-error"
|
|
2129
|
+
});
|
|
2130
|
+
};
|
|
2131
|
+
|
|
2132
|
+
//#endregion
|
|
2133
|
+
//#region src/server/http.ts
|
|
2134
|
+
/**
|
|
2135
|
+
* @fileoverview Standard API Handlers
|
|
2136
|
+
*
|
|
2137
|
+
* Provides standardized route handlers for AI streaming endpoints.
|
|
2138
|
+
* Ensures consistent error handling, telemetry, and headers.
|
|
2139
|
+
*
|
|
2140
|
+
* @module @od-oneapp/ai-platform/http/handlers
|
|
2141
|
+
*/
|
|
2142
|
+
/**
|
|
2143
|
+
* Creates a standard POST handler for AI chat.
|
|
2144
|
+
* Compatible with Next.js App Router route handlers.
|
|
2145
|
+
*
|
|
2146
|
+
* @example
|
|
2147
|
+
* ```ts
|
|
2148
|
+
* export const POST = createAIHandler({ model: openai('gpt-4') });
|
|
2149
|
+
* ```
|
|
2150
|
+
*/
|
|
2151
|
+
function createAIHandler(options) {
|
|
2152
|
+
return async function(req) {
|
|
2153
|
+
try {
|
|
2154
|
+
if (options.auth) {
|
|
2155
|
+
if (!await options.auth(req)) return new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
2156
|
+
status: 401,
|
|
2157
|
+
headers: { "Content-Type": "application/json" }
|
|
2158
|
+
});
|
|
2159
|
+
}
|
|
2160
|
+
const rawMessages = (await req.json()).messages;
|
|
2161
|
+
if (!Array.isArray(rawMessages)) return new Response(JSON.stringify({ error: "Invalid messages payload" }), {
|
|
2162
|
+
status: 400,
|
|
2163
|
+
headers: { "Content-Type": "application/json" }
|
|
2164
|
+
});
|
|
2165
|
+
const maxMessages = options.maxMessages ?? 100;
|
|
2166
|
+
const maxChars = options.maxChars ?? 2e5;
|
|
2167
|
+
const messageSchema = z.object({
|
|
2168
|
+
role: z.enum([
|
|
2169
|
+
"system",
|
|
2170
|
+
"user",
|
|
2171
|
+
"assistant",
|
|
2172
|
+
"tool"
|
|
2173
|
+
]),
|
|
2174
|
+
content: z.union([z.string(), z.array(z.unknown())])
|
|
2175
|
+
});
|
|
2176
|
+
if (!z.array(messageSchema).min(1).max(maxMessages).safeParse(rawMessages).success) return new Response(JSON.stringify({ error: "Invalid messages payload" }), {
|
|
2177
|
+
status: 400,
|
|
2178
|
+
headers: { "Content-Type": "application/json" }
|
|
2179
|
+
});
|
|
2180
|
+
if (rawMessages.reduce((sum, msg) => {
|
|
2181
|
+
if (msg && typeof msg === "object" && "content" in msg) {
|
|
2182
|
+
const { content } = msg;
|
|
2183
|
+
if (typeof content === "string") return sum + content.length;
|
|
2184
|
+
if (Array.isArray(content)) return sum + content.reduce((inner, part) => {
|
|
2185
|
+
if (part && typeof part === "object" && "text" in part) {
|
|
2186
|
+
const textValue = part.text;
|
|
2187
|
+
return inner + (typeof textValue === "string" ? textValue.length : 0);
|
|
2188
|
+
}
|
|
2189
|
+
return inner;
|
|
2190
|
+
}, 0);
|
|
2191
|
+
}
|
|
2192
|
+
return sum;
|
|
2193
|
+
}, 0) > maxChars) return new Response(JSON.stringify({ error: "Messages too large" }), {
|
|
2194
|
+
status: 413,
|
|
2195
|
+
headers: { "Content-Type": "application/json" }
|
|
2196
|
+
});
|
|
2197
|
+
let messages = rawMessages;
|
|
2198
|
+
if (options.compliance) {
|
|
2199
|
+
const { messages: processedMessages } = await preprocessMessages(messages, options.compliance);
|
|
2200
|
+
messages = processedMessages;
|
|
2201
|
+
}
|
|
2202
|
+
return (await streamText({
|
|
2203
|
+
model: options.compliance && isLanguageModelV3(options.model) ? wrapWithCompliance(options.model, options.compliance) : options.model,
|
|
2204
|
+
messages,
|
|
2205
|
+
system: options.system,
|
|
2206
|
+
onFinish: async ({ text }) => {
|
|
2207
|
+
if (options.onFinish) await options.onFinish(text);
|
|
2208
|
+
}
|
|
2209
|
+
})).toTextStreamResponse();
|
|
2210
|
+
} catch (error) {
|
|
2211
|
+
logError("AI Handler Error", { error: error instanceof Error ? error : new Error(String(error)) });
|
|
2212
|
+
return new Response(JSON.stringify({ error: "Internal Server Error" }), {
|
|
2213
|
+
status: 500,
|
|
2214
|
+
headers: { "Content-Type": "application/json" }
|
|
2215
|
+
});
|
|
2216
|
+
}
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
const isLanguageModelV3 = (model) => {
|
|
2220
|
+
return typeof model === "object" && model !== null && "specificationVersion" in model && model.specificationVersion === "v3";
|
|
2221
|
+
};
|
|
2222
|
+
|
|
2223
|
+
//#endregion
|
|
2224
|
+
export { STREAM_RESPONSE_OPTIONS, aggregateUsage, buildUIMessagePartsFromFinishEvent, calculateDuration, calculateModelCost, chainTransforms, checkHealth, collectDataParts, createAIHandler, createAgentRouteHandler, createCompactWindow, createDataPartExtractor, createExtendedWindow, createHealthCheckHandler, createHealthCheckMiddleware, createLargeWindow, createMessageConverter, createMessageWindow, createNextHealthCheckHandler, createOnFinishCallback, createStandardWindow, createTelemetryConfig, defaultGetMessageText, defaultValidateMessages, estimateInputTokens, extractFileAttachments, extractModelName, extractReasoningText, extractTelemetry, extractTextContent, filterByContent, filterByRole, formatConnectionError, formatErrorMessage, getErrorCode, getErrorMessage, getLastUserMessage, getLatestDataPart, groupDataPartsByType, handleRateLimitError, handleStreamResumption, inMemoryInitializeSession, initializeChatSession, initializePersistentSession, initializePersistentSessionFn, isRateLimitError, jsonError, jsonSuccess, mapStoredMessagesToUI, mapStoredToUIMessage, mergeSchemas, noopPersistMessage, noopSetupResumableStream, normalizeError, parseRequestBody, persistAssistantMessage, persistUserMessage, roundCost, setupResumableStream, transformContent, truncateText, validateMessages, wordBasedTokenizer };
|
|
2225
|
+
//# sourceMappingURL=server.mjs.map
|