glirastes 0.2.1 → 0.3.1
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/CHANGELOG.md +96 -0
- package/CODE_OF_CONDUCT.md +85 -0
- package/CONTRIBUTING.md +87 -0
- package/README.md +268 -23
- package/SECURITY.md +46 -0
- package/codegen/package.json +5 -0
- package/dist/cjs/codegen/asset-sync.d.ts +20 -0
- package/dist/cjs/codegen/asset-sync.js +28 -0
- package/dist/cjs/codegen/coverage.d.ts +66 -0
- package/dist/cjs/codegen/coverage.js +408 -0
- package/dist/cjs/codegen/endpoint-scanner.d.ts +16 -0
- package/dist/cjs/codegen/endpoint-scanner.js +197 -0
- package/dist/cjs/codegen/format.d.ts +17 -0
- package/dist/cjs/codegen/format.js +34 -0
- package/dist/cjs/codegen/fs-utils.d.ts +45 -0
- package/dist/cjs/codegen/fs-utils.js +165 -0
- package/dist/cjs/codegen/index.d.ts +13 -0
- package/dist/cjs/codegen/index.js +33 -0
- package/dist/cjs/codegen/mcp-generator.d.ts +50 -0
- package/dist/cjs/codegen/mcp-generator.js +803 -0
- package/dist/cjs/codegen/module-generator.d.ts +11 -0
- package/dist/cjs/codegen/module-generator.js +353 -0
- package/dist/cjs/codegen/nestjs-scanner.d.ts +43 -0
- package/dist/cjs/codegen/nestjs-scanner.js +241 -0
- package/dist/cjs/codegen/output-recommender.d.ts +23 -0
- package/dist/cjs/codegen/output-recommender.js +33 -0
- package/dist/cjs/codegen/scaffolder.d.ts +36 -0
- package/dist/cjs/codegen/scaffolder.js +262 -0
- package/dist/cjs/codegen/skill-generator.d.ts +49 -0
- package/dist/cjs/codegen/skill-generator.js +967 -0
- package/dist/cjs/codegen/types.d.ts +52 -0
- package/dist/cjs/codegen/types.js +16 -0
- package/dist/cjs/codegen/ui-scanner.d.ts +24 -0
- package/dist/cjs/codegen/ui-scanner.js +202 -0
- package/dist/cjs/codegen/validator.d.ts +35 -0
- package/dist/cjs/codegen/validator.js +292 -0
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +23 -0
- package/dist/cjs/openapi/index.d.ts +78 -0
- package/dist/cjs/openapi/index.js +347 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/react/audio-worklet-processor.d.ts +16 -0
- package/dist/cjs/react/audio-worklet-processor.js +27 -0
- package/dist/cjs/react/bridges/rest-session-bridge.d.ts +113 -0
- package/dist/cjs/react/bridges/rest-session-bridge.js +209 -0
- package/dist/cjs/react/components/ai-chat-panel.d.ts +12 -0
- package/dist/cjs/react/components/ai-chat-panel.js +67 -0
- package/dist/cjs/react/components/ai-trigger-button.d.ts +2 -0
- package/dist/cjs/react/components/ai-trigger-button.js +239 -0
- package/dist/cjs/react/components/approval-card.d.ts +9 -0
- package/dist/cjs/react/components/approval-card.js +24 -0
- package/dist/cjs/react/components/bulk-approval-card.d.ts +9 -0
- package/dist/cjs/react/components/bulk-approval-card.js +36 -0
- package/dist/cjs/react/components/chat-input.d.ts +10 -0
- package/dist/cjs/react/components/chat-input.js +60 -0
- package/dist/cjs/react/components/chat-window.d.ts +2 -0
- package/dist/cjs/react/components/chat-window.js +134 -0
- package/dist/cjs/react/components/confirm-dialog.d.ts +32 -0
- package/dist/cjs/react/components/confirm-dialog.js +48 -0
- package/dist/cjs/react/components/floating-chat-ui.d.ts +90 -0
- package/dist/cjs/react/components/floating-chat-ui.js +163 -0
- package/dist/cjs/react/components/mention-chip.d.ts +16 -0
- package/dist/cjs/react/components/mention-chip.js +42 -0
- package/dist/cjs/react/components/mention-input.d.ts +8 -0
- package/dist/cjs/react/components/mention-input.js +117 -0
- package/dist/cjs/react/components/message-bubble.d.ts +11 -0
- package/dist/cjs/react/components/message-bubble.js +171 -0
- package/dist/cjs/react/components/message-list.d.ts +8 -0
- package/dist/cjs/react/components/message-list.js +52 -0
- package/dist/cjs/react/components/pipeline-timeline.d.ts +7 -0
- package/dist/cjs/react/components/pipeline-timeline.js +72 -0
- package/dist/cjs/react/components/recording-bar.d.ts +2 -0
- package/dist/cjs/react/components/recording-bar.js +81 -0
- package/dist/cjs/react/components/recording-controls.d.ts +18 -0
- package/dist/cjs/react/components/recording-controls.js +38 -0
- package/dist/cjs/react/components/rich-mention-input.d.ts +2 -0
- package/dist/cjs/react/components/rich-mention-input.js +571 -0
- package/dist/cjs/react/components/session-switcher.d.ts +14 -0
- package/dist/cjs/react/components/session-switcher.js +133 -0
- package/dist/cjs/react/components/suggestion-bar.d.ts +9 -0
- package/dist/cjs/react/components/suggestion-bar.js +47 -0
- package/dist/cjs/react/components/suggestion-chip.d.ts +9 -0
- package/dist/cjs/react/components/suggestion-chip.js +14 -0
- package/dist/cjs/react/components/voice-input-addon.d.ts +13 -0
- package/dist/cjs/react/components/voice-input-addon.js +31 -0
- package/dist/cjs/react/components/voice-input-button.d.ts +2 -0
- package/dist/cjs/react/components/voice-input-button.js +55 -0
- package/dist/cjs/react/components/wavesurfer-loader.d.ts +5 -0
- package/dist/cjs/react/components/wavesurfer-loader.js +36 -0
- package/dist/cjs/react/hooks/use-ai-chat.d.ts +22 -0
- package/dist/cjs/react/hooks/use-ai-chat.js +51 -0
- package/dist/cjs/react/hooks/use-approvals.d.ts +23 -0
- package/dist/cjs/react/hooks/use-approvals.js +63 -0
- package/dist/cjs/react/hooks/use-deepgram-transcription.d.ts +2 -0
- package/dist/cjs/react/hooks/use-deepgram-transcription.js +329 -0
- package/dist/cjs/react/hooks/use-drag-resize-panel.d.ts +48 -0
- package/dist/cjs/react/hooks/use-drag-resize-panel.js +308 -0
- package/dist/cjs/react/hooks/use-draggable-position.d.ts +25 -0
- package/dist/cjs/react/hooks/use-draggable-position.js +134 -0
- package/dist/cjs/react/hooks/use-keyboard-shortcut.d.ts +20 -0
- package/dist/cjs/react/hooks/use-keyboard-shortcut.js +93 -0
- package/dist/cjs/react/hooks/use-mentions.d.ts +23 -0
- package/dist/cjs/react/hooks/use-mentions.js +42 -0
- package/dist/cjs/react/hooks/use-suggestions.d.ts +22 -0
- package/dist/cjs/react/hooks/use-suggestions.js +39 -0
- package/dist/cjs/react/index.d.ts +43 -0
- package/dist/cjs/react/index.js +111 -0
- package/dist/cjs/react/provider/ai-chat-provider.d.ts +45 -0
- package/dist/cjs/react/provider/ai-chat-provider.js +1024 -0
- package/dist/cjs/react/provider/chat-context.d.ts +84 -0
- package/dist/cjs/react/provider/chat-context.js +13 -0
- package/dist/cjs/react/template.d.ts +84 -0
- package/dist/cjs/react/template.js +90 -0
- package/dist/cjs/react/themes.d.ts +24 -0
- package/dist/cjs/react/themes.js +273 -0
- package/dist/cjs/react/transports/langgraph/index.d.ts +2 -0
- package/dist/cjs/react/transports/langgraph/index.js +8 -0
- package/dist/cjs/react/transports/langgraph/langgraph-ai-chat.d.ts +62 -0
- package/dist/cjs/react/transports/langgraph/langgraph-ai-chat.js +94 -0
- package/dist/cjs/react/transports/langgraph.d.ts +71 -0
- package/dist/cjs/react/transports/langgraph.js +369 -0
- package/dist/cjs/react/transports/vercel/index.d.ts +2 -0
- package/dist/cjs/react/transports/vercel/index.js +8 -0
- package/dist/cjs/react/transports/vercel/vercel-ai-chat.d.ts +49 -0
- package/dist/cjs/react/transports/vercel/vercel-ai-chat.js +81 -0
- package/dist/cjs/react/transports/vercel.d.ts +30 -0
- package/dist/cjs/react/transports/vercel.js +69 -0
- package/dist/cjs/react/types.d.ts +760 -0
- package/dist/cjs/react/types.js +2 -0
- package/dist/cjs/react/ui.d.ts +73 -0
- package/dist/cjs/react/ui.js +183 -0
- package/dist/cjs/react/utils/create-approval-description.d.ts +53 -0
- package/dist/cjs/react/utils/create-approval-description.js +122 -0
- package/dist/cjs/react/utils/create-tool-result-handler.d.ts +90 -0
- package/dist/cjs/react/utils/create-tool-result-handler.js +168 -0
- package/dist/cjs/react/utils/mention-markup.d.ts +13 -0
- package/dist/cjs/react/utils/mention-markup.js +117 -0
- package/dist/cjs/react/utils/merge-pipeline-steps.d.ts +19 -0
- package/dist/cjs/react/utils/merge-pipeline-steps.js +81 -0
- package/dist/cjs/react/utils/sanitize-stored-messages.d.ts +23 -0
- package/dist/cjs/react/utils/sanitize-stored-messages.js +52 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat-auth.guard.d.ts +10 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat-auth.guard.js +42 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat-module-options.interface.d.ts +32 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat-module-options.interface.js +4 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat.controller.d.ts +11 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat.controller.js +80 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat.module.d.ts +9 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat.module.js +119 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat.service.d.ts +13 -0
- package/dist/cjs/server/adapters/nestjs/chat/ai-chat.service.js +131 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/ai-chat-request.dto.d.ts +7 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/ai-chat-request.dto.js +52 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message-part.dto.d.ts +19 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message-part.dto.js +108 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message.dto.d.ts +8 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/chat-message.dto.js +59 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/index.d.ts +3 -0
- package/dist/cjs/server/adapters/nestjs/chat/dto/index.js +9 -0
- package/dist/cjs/server/adapters/nestjs/chat/index.d.ts +3 -0
- package/dist/cjs/server/adapters/nestjs/chat/index.js +9 -0
- package/dist/cjs/server/adapters/nestjs/chat/speech-stream.gateway.d.ts +4 -0
- package/dist/cjs/server/adapters/nestjs/chat/speech-stream.gateway.js +73 -0
- package/dist/cjs/server/adapters/nestjs/chat/ws-loader.d.ts +5 -0
- package/dist/cjs/server/adapters/nestjs/chat/ws-loader.js +72 -0
- package/dist/cjs/server/adapters/nestjs/decorators/ai-module.decorator.d.ts +6 -0
- package/dist/cjs/server/adapters/nestjs/decorators/ai-module.decorator.js +13 -0
- package/dist/cjs/server/adapters/nestjs/decorators/ai-param.decorator.d.ts +5 -0
- package/dist/cjs/server/adapters/nestjs/decorators/ai-param.decorator.js +15 -0
- package/dist/cjs/server/adapters/nestjs/decorators/ai-tool.decorator.d.ts +6 -0
- package/dist/cjs/server/adapters/nestjs/decorators/ai-tool.decorator.js +22 -0
- package/dist/cjs/server/adapters/nestjs/decorators/constants.d.ts +4 -0
- package/dist/cjs/server/adapters/nestjs/decorators/constants.js +7 -0
- package/dist/cjs/server/adapters/nestjs/index.d.ts +14 -0
- package/dist/cjs/server/adapters/nestjs/index.js +42 -0
- package/dist/cjs/server/adapters/nestjs/metadata.d.ts +39 -0
- package/dist/cjs/server/adapters/nestjs/metadata.js +36 -0
- package/dist/cjs/server/adapters/nestjs/module/ai-tools-explorer.service.d.ts +36 -0
- package/dist/cjs/server/adapters/nestjs/module/ai-tools-explorer.service.js +159 -0
- package/dist/cjs/server/adapters/nestjs/module/ai-tools.module.d.ts +37 -0
- package/dist/cjs/server/adapters/nestjs/module/ai-tools.module.js +44 -0
- package/dist/cjs/server/adapters/nestjs/module/ai-tools.service.d.ts +28 -0
- package/dist/cjs/server/adapters/nestjs/module/ai-tools.service.js +41 -0
- package/dist/cjs/server/adapters/nestjs/scanner/nestjs-scanner.d.ts +16 -0
- package/dist/cjs/server/adapters/nestjs/scanner/nestjs-scanner.js +117 -0
- package/dist/cjs/server/adapters/nestjs/scanner/schema-builder.d.ts +26 -0
- package/dist/cjs/server/adapters/nestjs/scanner/schema-builder.js +259 -0
- package/dist/cjs/server/adapters/nestjs/swagger.d.ts +7 -0
- package/dist/cjs/server/adapters/nestjs/swagger.js +39 -0
- package/dist/cjs/server/adapters/nextjs/context-helpers.d.ts +22 -0
- package/dist/cjs/server/adapters/nextjs/context-helpers.js +60 -0
- package/dist/cjs/server/adapters/nextjs/create-ai-chat-handler.d.ts +94 -0
- package/dist/cjs/server/adapters/nextjs/create-ai-chat-handler.js +965 -0
- package/dist/cjs/server/adapters/nextjs/filter-fallback-error.d.ts +11 -0
- package/dist/cjs/server/adapters/nextjs/filter-fallback-error.js +41 -0
- package/dist/cjs/server/adapters/nextjs/index.d.ts +8 -0
- package/dist/cjs/server/adapters/nextjs/index.js +22 -0
- package/dist/cjs/server/adapters/nextjs/prepare-model-messages.d.ts +58 -0
- package/dist/cjs/server/adapters/nextjs/prepare-model-messages.js +114 -0
- package/dist/cjs/server/adapters/nextjs/sanitize-model-messages.d.ts +64 -0
- package/dist/cjs/server/adapters/nextjs/sanitize-model-messages.js +489 -0
- package/dist/cjs/server/adapters/nextjs/server-action-helper.d.ts +7 -0
- package/dist/cjs/server/adapters/nextjs/server-action-helper.js +40 -0
- package/dist/cjs/server/adapters/nextjs/types.d.ts +246 -0
- package/dist/cjs/server/adapters/nextjs/types.js +2 -0
- package/dist/cjs/server/adapters/nextjs/with-ai-tool.d.ts +9 -0
- package/dist/cjs/server/adapters/nextjs/with-ai-tool.js +12 -0
- package/dist/cjs/server/core/followup-tool.d.ts +9 -0
- package/dist/cjs/server/core/followup-tool.js +82 -0
- package/dist/cjs/server/core/index.d.ts +139 -0
- package/dist/cjs/server/core/index.js +818 -0
- package/dist/cjs/server/core/intent-classifier.d.ts +94 -0
- package/dist/cjs/server/core/intent-classifier.js +324 -0
- package/dist/cjs/server/core/model-selector.d.ts +12 -0
- package/dist/cjs/server/core/model-selector.js +48 -0
- package/dist/cjs/server/core/module-router.d.ts +14 -0
- package/dist/cjs/server/core/module-router.js +110 -0
- package/dist/cjs/server/core/zod-compact.d.ts +18 -0
- package/dist/cjs/server/core/zod-compact.js +129 -0
- package/dist/cjs/server/index.d.ts +6 -0
- package/dist/cjs/server/index.js +28 -0
- package/dist/cjs/server/lancer/cache.d.ts +9 -0
- package/dist/cjs/server/lancer/cache.js +33 -0
- package/dist/cjs/server/lancer/client.d.ts +2 -0
- package/dist/cjs/server/lancer/client.js +279 -0
- package/dist/cjs/server/lancer/errors.d.ts +13 -0
- package/dist/cjs/server/lancer/errors.js +21 -0
- package/dist/cjs/server/lancer/index.d.ts +3 -0
- package/dist/cjs/server/lancer/index.js +7 -0
- package/dist/cjs/server/lancer/telemetry-buffer.d.ts +15 -0
- package/dist/cjs/server/lancer/telemetry-buffer.js +64 -0
- package/dist/cjs/server/lancer/types.d.ts +201 -0
- package/dist/cjs/server/lancer/types.js +5 -0
- package/dist/cjs/server/pii-shield/aegis-detector.d.ts +26 -0
- package/dist/cjs/server/pii-shield/aegis-detector.js +26 -0
- package/dist/cjs/server/pii-shield/create-pii-shield.d.ts +2 -0
- package/dist/cjs/server/pii-shield/create-pii-shield.js +191 -0
- package/dist/cjs/server/pii-shield/deep-walk.d.ts +1 -0
- package/dist/cjs/server/pii-shield/deep-walk.js +19 -0
- package/dist/cjs/server/pii-shield/fake-data.d.ts +5 -0
- package/dist/cjs/server/pii-shield/fake-data.js +170 -0
- package/dist/cjs/server/pii-shield/index.d.ts +4 -0
- package/dist/cjs/server/pii-shield/index.js +9 -0
- package/dist/cjs/server/pii-shield/leakage.d.ts +1 -0
- package/dist/cjs/server/pii-shield/leakage.js +15 -0
- package/dist/cjs/server/pii-shield/session-mapping.d.ts +12 -0
- package/dist/cjs/server/pii-shield/session-mapping.js +70 -0
- package/dist/cjs/server/pipeline.d.ts +26 -0
- package/dist/cjs/server/pipeline.js +127 -0
- package/dist/cjs/server/testing/approvals.d.ts +11 -0
- package/dist/cjs/server/testing/approvals.js +41 -0
- package/dist/cjs/server/testing/combination.d.ts +3 -0
- package/dist/cjs/server/testing/combination.js +83 -0
- package/dist/cjs/server/testing/create-test-suite.d.ts +29 -0
- package/dist/cjs/server/testing/create-test-suite.js +81 -0
- package/dist/cjs/server/testing/edge-cases.d.ts +8 -0
- package/dist/cjs/server/testing/edge-cases.js +277 -0
- package/dist/cjs/server/testing/guardrails.d.ts +14 -0
- package/dist/cjs/server/testing/guardrails.js +152 -0
- package/dist/cjs/server/testing/helpers.d.ts +14 -0
- package/dist/cjs/server/testing/helpers.js +119 -0
- package/dist/cjs/server/testing/index.d.ts +12 -0
- package/dist/cjs/server/testing/index.js +37 -0
- package/dist/cjs/server/testing/mock-executor.d.ts +8 -0
- package/dist/cjs/server/testing/mock-executor.js +40 -0
- package/dist/cjs/server/testing/regression.d.ts +7 -0
- package/dist/cjs/server/testing/regression.js +27 -0
- package/dist/cjs/server/testing/routing.d.ts +12 -0
- package/dist/cjs/server/testing/routing.js +63 -0
- package/dist/cjs/server/testing/schema-consistency.d.ts +6 -0
- package/dist/cjs/server/testing/schema-consistency.js +56 -0
- package/dist/cjs/server/testing/smoke.d.ts +11 -0
- package/dist/cjs/server/testing/smoke.js +64 -0
- package/dist/cjs/server/testing/types.d.ts +156 -0
- package/dist/cjs/server/testing/types.js +2 -0
- package/dist/cjs/types.d.ts +696 -0
- package/dist/cjs/types.js +91 -0
- package/dist/cli/commands/install-skills.d.ts +17 -0
- package/dist/cli/commands/install-skills.js +185 -0
- package/dist/cli/commands/install-skills.js.map +1 -0
- package/dist/cli/index.js +25 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/react/components/ai-trigger-button.js +4 -6
- package/dist/react/components/ai-trigger-button.js.map +1 -1
- package/dist/react/components/recording-bar.js +4 -6
- package/dist/react/components/recording-bar.js.map +1 -1
- package/dist/react/components/wavesurfer-loader.d.ts +5 -0
- package/dist/react/components/wavesurfer-loader.js +34 -0
- package/dist/react/components/wavesurfer-loader.js.map +1 -0
- package/dist/server/adapters/nestjs/chat/ai-chat.module.d.ts +1 -1
- package/dist/server/adapters/nestjs/chat/ai-chat.module.js +3 -2
- package/dist/server/adapters/nestjs/chat/ai-chat.module.js.map +1 -1
- package/dist/server/adapters/nestjs/chat/speech-stream.gateway.d.ts +1 -1
- package/dist/server/adapters/nestjs/chat/speech-stream.gateway.js +10 -8
- package/dist/server/adapters/nestjs/chat/speech-stream.gateway.js.map +1 -1
- package/dist/server/adapters/nestjs/chat/ws-loader.d.ts +5 -0
- package/dist/server/adapters/nestjs/chat/ws-loader.js +37 -0
- package/dist/server/adapters/nestjs/chat/ws-loader.js.map +1 -0
- package/dist/server/adapters/nextjs/create-ai-chat-handler.d.ts +16 -0
- package/dist/server/adapters/nextjs/create-ai-chat-handler.js +14 -3
- package/dist/server/adapters/nextjs/create-ai-chat-handler.js.map +1 -1
- package/dist/server/adapters/nextjs/filter-fallback-error.d.ts +11 -0
- package/dist/server/adapters/nextjs/filter-fallback-error.js +38 -0
- package/dist/server/adapters/nextjs/filter-fallback-error.js.map +1 -0
- package/openapi/package.json +5 -0
- package/package.json +66 -21
- package/react/langgraph/package.json +5 -0
- package/react/package.json +5 -0
- package/react/vercel/package.json +5 -0
- package/server/nestjs/package.json +5 -0
- package/server/nextjs/package.json +5 -0
- package/server/package.json +5 -0
- package/server/testing/package.json +5 -0
- package/skills/README.md +107 -0
- package/skills/building-glirastes-chat-ui/SKILL.md +378 -0
- package/skills/integrating-glirastes-nestjs/SKILL.md +346 -0
- package/skills/integrating-glirastes-nextjs/SKILL.md +306 -0
- package/skills/integrating-glirastes-nextjs-with-nestjs-backend/SKILL.md +359 -0
- package/skills/maintaining-glirastes-tools/SKILL.md +463 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { IntentClassifier, IntentClassifierConfig, IntentClassification, ModuleDefinition, ConversationMessage } from '../../types.js';
|
|
2
|
+
import { type Stemmer } from 'snowball-stemmers';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for the local intent classifier.
|
|
5
|
+
*
|
|
6
|
+
* Extends IntentClassifierConfig with a `generateText` callback so
|
|
7
|
+
* consumers can pass in their AI SDK `generateText` function without
|
|
8
|
+
* the server-core package needing a hard dependency on `ai`.
|
|
9
|
+
*/
|
|
10
|
+
export interface LocalClassifierConfig extends IntentClassifierConfig {
|
|
11
|
+
/**
|
|
12
|
+
* Optional generateText callback (from Vercel AI SDK or compatible).
|
|
13
|
+
* When provided, the classifier uses LLM classification with a
|
|
14
|
+
* Jaccard heuristic fallback. Without it, only heuristics are used.
|
|
15
|
+
*
|
|
16
|
+
* Expected signature:
|
|
17
|
+
* ```ts
|
|
18
|
+
* (options: { model: unknown; prompt: string; }) => Promise<{ text: string }>
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
generateText?: (options: {
|
|
22
|
+
model: unknown;
|
|
23
|
+
prompt: string;
|
|
24
|
+
abortSignal?: AbortSignal;
|
|
25
|
+
}) => Promise<{
|
|
26
|
+
text: string;
|
|
27
|
+
}>;
|
|
28
|
+
}
|
|
29
|
+
/** Resolve a BCP-47 locale (e.g. "de-DE") to a Snowball Stemmer, or null */
|
|
30
|
+
export declare function stemmerForLocale(locale?: string): Stemmer | null;
|
|
31
|
+
/**
|
|
32
|
+
* Tokenize text into lowercase tokens (3+ chars).
|
|
33
|
+
* Strips punctuation and splits on whitespace.
|
|
34
|
+
*/
|
|
35
|
+
export declare function tokenize(text: string): Set<string>;
|
|
36
|
+
/** Character trigrams for fuzzy matching */
|
|
37
|
+
export declare function charTrigrams(text: string): Set<string>;
|
|
38
|
+
/**
|
|
39
|
+
* Compute Jaccard similarity between two token sets.
|
|
40
|
+
* Returns 0 when either set is empty.
|
|
41
|
+
*/
|
|
42
|
+
export declare function jaccardSimilarity(a: Set<string>, b: Set<string>): number;
|
|
43
|
+
/** Count token pairs sharing a prefix of at least `minLen` chars */
|
|
44
|
+
export declare function prefixOverlap(a: Set<string>, b: Set<string>, minLen?: number): number;
|
|
45
|
+
/**
|
|
46
|
+
* Classify user input against modules using multi-signal similarity:
|
|
47
|
+
* 1. Snowball-stemmed Jaccard (language-aware inflection handling)
|
|
48
|
+
* 2. Prefix overlap (morphological variants)
|
|
49
|
+
* 3. Character trigrams (typos, fuzzy matches)
|
|
50
|
+
*
|
|
51
|
+
* Maps raw scores to confidence buckets:
|
|
52
|
+
* - >= 0.5 → 0.93 (high confidence)
|
|
53
|
+
* - >= 0.4 → 0.86 (medium-high)
|
|
54
|
+
* - >= 0.15 → 0.72 (medium)
|
|
55
|
+
* - < 0.15 → ambiguous
|
|
56
|
+
*/
|
|
57
|
+
export declare function classifyWithHeuristics(message: string, modules: ModuleDefinition[], locale?: string): IntentClassification;
|
|
58
|
+
/**
|
|
59
|
+
* Build the classification prompt for LLM-based intent classification.
|
|
60
|
+
*/
|
|
61
|
+
export declare function buildClassificationPrompt(message: string, modules: ModuleDefinition[], history?: ConversationMessage[]): string;
|
|
62
|
+
/**
|
|
63
|
+
* Parse a JSON response from the LLM, tolerating markdown code blocks
|
|
64
|
+
* and other wrapper text.
|
|
65
|
+
*/
|
|
66
|
+
export declare function parseJsonResponse(text: string): {
|
|
67
|
+
moduleId: string;
|
|
68
|
+
confidence: number;
|
|
69
|
+
} | null;
|
|
70
|
+
/**
|
|
71
|
+
* Create a local intent classifier that uses the user's own model for
|
|
72
|
+
* LLM classification, falling back to a multi-signal heuristic when
|
|
73
|
+
* the LLM is unavailable or fails.
|
|
74
|
+
*
|
|
75
|
+
* Heuristics use Snowball stemming (24 languages), prefix overlap, and
|
|
76
|
+
* character trigrams for language-agnostic fuzzy matching.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```ts
|
|
80
|
+
* import { generateText } from 'ai';
|
|
81
|
+
* import { openai } from '@ai-sdk/openai';
|
|
82
|
+
*
|
|
83
|
+
* const classifier = createIntentClassifier({
|
|
84
|
+
* modules: [taskModule, calendarModule],
|
|
85
|
+
* model: openai('gpt-4o-mini'),
|
|
86
|
+
* generateText,
|
|
87
|
+
* locale: 'de',
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* const result = await classifier.classify('create a new task');
|
|
91
|
+
* // { intent: 'tasks', confidence: 0.95 }
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare function createIntentClassifier(config: LocalClassifierConfig): IntentClassifier;
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.stemmerForLocale = stemmerForLocale;
|
|
4
|
+
exports.tokenize = tokenize;
|
|
5
|
+
exports.charTrigrams = charTrigrams;
|
|
6
|
+
exports.jaccardSimilarity = jaccardSimilarity;
|
|
7
|
+
exports.prefixOverlap = prefixOverlap;
|
|
8
|
+
exports.classifyWithHeuristics = classifyWithHeuristics;
|
|
9
|
+
exports.buildClassificationPrompt = buildClassificationPrompt;
|
|
10
|
+
exports.parseJsonResponse = parseJsonResponse;
|
|
11
|
+
exports.createIntentClassifier = createIntentClassifier;
|
|
12
|
+
const snowball_stemmers_1 = require("snowball-stemmers");
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Locale → Snowball Stemmer
|
|
15
|
+
// ============================================================================
|
|
16
|
+
const AVAILABLE_ALGOS = new Set((0, snowball_stemmers_1.algorithms)());
|
|
17
|
+
const LOCALE_MAP = {
|
|
18
|
+
ar: 'arabic', am: 'armenian', eu: 'basque', ca: 'catalan', cs: 'czech',
|
|
19
|
+
da: 'danish', nl: 'dutch', en: 'english', fi: 'finnish', fr: 'french',
|
|
20
|
+
de: 'german', hu: 'hungarian', it: 'italian', ga: 'irish', nb: 'norwegian',
|
|
21
|
+
nn: 'norwegian', no: 'norwegian', pt: 'portuguese', ro: 'romanian',
|
|
22
|
+
ru: 'russian', es: 'spanish', sl: 'slovene', sv: 'swedish', ta: 'tamil',
|
|
23
|
+
tr: 'turkish',
|
|
24
|
+
};
|
|
25
|
+
/** Resolve a BCP-47 locale (e.g. "de-DE") to a Snowball Stemmer, or null */
|
|
26
|
+
function stemmerForLocale(locale) {
|
|
27
|
+
if (!locale)
|
|
28
|
+
return null;
|
|
29
|
+
const lang = locale.split(/[-_]/)[0].toLowerCase();
|
|
30
|
+
const algo = LOCALE_MAP[lang];
|
|
31
|
+
if (algo && AVAILABLE_ALGOS.has(algo))
|
|
32
|
+
return (0, snowball_stemmers_1.newStemmer)(algo);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Tokenization & Similarity
|
|
37
|
+
// ============================================================================
|
|
38
|
+
/**
|
|
39
|
+
* Tokenize text into lowercase tokens (3+ chars).
|
|
40
|
+
* Strips punctuation and splits on whitespace.
|
|
41
|
+
*/
|
|
42
|
+
function tokenize(text) {
|
|
43
|
+
return new Set(text
|
|
44
|
+
.toLowerCase()
|
|
45
|
+
.replace(/[^\p{L}\p{N}\s]+/gu, ' ')
|
|
46
|
+
.split(/\s+/)
|
|
47
|
+
.map((t) => t.trim())
|
|
48
|
+
.filter((t) => t.length >= 3));
|
|
49
|
+
}
|
|
50
|
+
function rawTokenize(text) {
|
|
51
|
+
return text
|
|
52
|
+
.toLowerCase()
|
|
53
|
+
.replace(/[^\p{L}\p{N}\s]+/gu, ' ')
|
|
54
|
+
.split(/\s+/)
|
|
55
|
+
.map((t) => t.trim())
|
|
56
|
+
.filter((t) => t.length >= 3);
|
|
57
|
+
}
|
|
58
|
+
function toSet(tokens) {
|
|
59
|
+
return new Set(tokens);
|
|
60
|
+
}
|
|
61
|
+
function stemTokens(tokens, stemmer) {
|
|
62
|
+
if (!stemmer)
|
|
63
|
+
return toSet(tokens);
|
|
64
|
+
return toSet(tokens.map((t) => stemmer.stem(t)));
|
|
65
|
+
}
|
|
66
|
+
/** Character trigrams for fuzzy matching */
|
|
67
|
+
function charTrigrams(text) {
|
|
68
|
+
const clean = text.toLowerCase().replace(/[^\p{L}\p{N}]/gu, '');
|
|
69
|
+
const grams = new Set();
|
|
70
|
+
for (let i = 0; i <= clean.length - 3; i++) {
|
|
71
|
+
grams.add(clean.slice(i, i + 3));
|
|
72
|
+
}
|
|
73
|
+
return grams;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Compute Jaccard similarity between two token sets.
|
|
77
|
+
* Returns 0 when either set is empty.
|
|
78
|
+
*/
|
|
79
|
+
function jaccardSimilarity(a, b) {
|
|
80
|
+
if (a.size === 0 || b.size === 0)
|
|
81
|
+
return 0;
|
|
82
|
+
let intersection = 0;
|
|
83
|
+
const [small, large] = a.size <= b.size ? [a, b] : [b, a];
|
|
84
|
+
for (const token of small) {
|
|
85
|
+
if (large.has(token))
|
|
86
|
+
intersection++;
|
|
87
|
+
}
|
|
88
|
+
const union = a.size + b.size - intersection;
|
|
89
|
+
return union === 0 ? 0 : intersection / union;
|
|
90
|
+
}
|
|
91
|
+
/** Count token pairs sharing a prefix of at least `minLen` chars */
|
|
92
|
+
function prefixOverlap(a, b, minLen = 4) {
|
|
93
|
+
if (a.size === 0 || b.size === 0)
|
|
94
|
+
return 0;
|
|
95
|
+
const bArr = [...b];
|
|
96
|
+
let matches = 0;
|
|
97
|
+
const used = new Set();
|
|
98
|
+
for (const tokA of a) {
|
|
99
|
+
if (tokA.length < minLen)
|
|
100
|
+
continue;
|
|
101
|
+
const pre = tokA.slice(0, minLen);
|
|
102
|
+
for (let j = 0; j < bArr.length; j++) {
|
|
103
|
+
if (used.has(j) || bArr[j].length < minLen)
|
|
104
|
+
continue;
|
|
105
|
+
if (bArr[j].startsWith(pre)) {
|
|
106
|
+
matches++;
|
|
107
|
+
used.add(j);
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const union = a.size + b.size - matches;
|
|
113
|
+
return union === 0 ? 0 : matches / union;
|
|
114
|
+
}
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// Multi-Signal Heuristic Classifier
|
|
117
|
+
// ============================================================================
|
|
118
|
+
function scoreModule(inputTokens, inputStems, inputTrigrams, m, stemmer) {
|
|
119
|
+
const descRaw = rawTokenize(m.description);
|
|
120
|
+
const descTokens = toSet(descRaw);
|
|
121
|
+
const descStems = stemTokens(descRaw, stemmer);
|
|
122
|
+
const descTrigrams = charTrigrams(m.description);
|
|
123
|
+
const examples = m.classification?.examples ?? [];
|
|
124
|
+
// --- Signal 1: Stemmed Jaccard (handles inflection via Snowball) ---
|
|
125
|
+
let bestStemEx = 0;
|
|
126
|
+
for (const ex of examples) {
|
|
127
|
+
const exRaw = rawTokenize(ex);
|
|
128
|
+
bestStemEx = Math.max(bestStemEx, jaccardSimilarity(inputStems, stemTokens(exRaw, stemmer)));
|
|
129
|
+
}
|
|
130
|
+
const stemmedScore = Math.max(bestStemEx, jaccardSimilarity(inputStems, descStems) * 0.8);
|
|
131
|
+
// --- Signal 2: Prefix overlap (morphological variants beyond stemmer) ---
|
|
132
|
+
let bestPrefixEx = 0;
|
|
133
|
+
for (const ex of examples) {
|
|
134
|
+
bestPrefixEx = Math.max(bestPrefixEx, prefixOverlap(inputTokens, toSet(rawTokenize(ex))));
|
|
135
|
+
}
|
|
136
|
+
const prefixScore = Math.max(bestPrefixEx, prefixOverlap(inputTokens, descTokens) * 0.8);
|
|
137
|
+
// --- Signal 3: Character trigrams (typos, partial matches) ---
|
|
138
|
+
let bestTrigramEx = 0;
|
|
139
|
+
for (const ex of examples) {
|
|
140
|
+
bestTrigramEx = Math.max(bestTrigramEx, jaccardSimilarity(inputTrigrams, charTrigrams(ex)));
|
|
141
|
+
}
|
|
142
|
+
const trigramScore = Math.max(bestTrigramEx, jaccardSimilarity(inputTrigrams, descTrigrams) * 0.8);
|
|
143
|
+
// Best signal wins (slight discount on fuzzier methods)
|
|
144
|
+
return Math.max(stemmedScore, prefixScore * 0.95, trigramScore * 0.85);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Classify user input against modules using multi-signal similarity:
|
|
148
|
+
* 1. Snowball-stemmed Jaccard (language-aware inflection handling)
|
|
149
|
+
* 2. Prefix overlap (morphological variants)
|
|
150
|
+
* 3. Character trigrams (typos, fuzzy matches)
|
|
151
|
+
*
|
|
152
|
+
* Maps raw scores to confidence buckets:
|
|
153
|
+
* - >= 0.5 → 0.93 (high confidence)
|
|
154
|
+
* - >= 0.4 → 0.86 (medium-high)
|
|
155
|
+
* - >= 0.15 → 0.72 (medium)
|
|
156
|
+
* - < 0.15 → ambiguous
|
|
157
|
+
*/
|
|
158
|
+
function classifyWithHeuristics(message, modules, locale) {
|
|
159
|
+
const stemmer = stemmerForLocale(locale);
|
|
160
|
+
const raw = rawTokenize(message);
|
|
161
|
+
const inputTokens = toSet(raw);
|
|
162
|
+
const inputStems = stemTokens(raw, stemmer);
|
|
163
|
+
const inputTrigrams = charTrigrams(message);
|
|
164
|
+
if (inputTokens.size === 0 || modules.length === 0) {
|
|
165
|
+
return { intent: 'ambiguous', confidence: 0 };
|
|
166
|
+
}
|
|
167
|
+
const scored = modules.map((m) => ({
|
|
168
|
+
moduleId: m.id,
|
|
169
|
+
score: scoreModule(inputTokens, inputStems, inputTrigrams, m, stemmer),
|
|
170
|
+
}));
|
|
171
|
+
scored.sort((a, b) => b.score - a.score);
|
|
172
|
+
const best = scored[0];
|
|
173
|
+
if (!best || best.score < 0.15) {
|
|
174
|
+
return { intent: 'ambiguous', confidence: 0 };
|
|
175
|
+
}
|
|
176
|
+
// Map raw Jaccard score to confidence buckets
|
|
177
|
+
const confidence = best.score >= 0.5 ? 0.93 : best.score >= 0.4 ? 0.86 : 0.72;
|
|
178
|
+
return { intent: best.moduleId, confidence };
|
|
179
|
+
}
|
|
180
|
+
// ============================================================================
|
|
181
|
+
// LLM Classification
|
|
182
|
+
// ============================================================================
|
|
183
|
+
/**
|
|
184
|
+
* Build the classification prompt for LLM-based intent classification.
|
|
185
|
+
*/
|
|
186
|
+
function buildClassificationPrompt(message, modules, history) {
|
|
187
|
+
const moduleDescriptions = modules
|
|
188
|
+
.map((m) => {
|
|
189
|
+
const examples = m.classification?.examples ?? [];
|
|
190
|
+
const examplesStr = examples.length > 0
|
|
191
|
+
? ` Examples: ${examples.map((e) => `"${e}"`).join(', ')}`
|
|
192
|
+
: '';
|
|
193
|
+
return `- ${m.id}: ${m.description}.${examplesStr}`;
|
|
194
|
+
})
|
|
195
|
+
.join('\n');
|
|
196
|
+
const historyContext = history && history.length > 0
|
|
197
|
+
? `\nConversation history:\n${history.map((h) => `${h.role}: ${h.content}`).join('\n')}\n`
|
|
198
|
+
: '';
|
|
199
|
+
return `You are an intent classifier. Given a user message, determine which module best handles it.
|
|
200
|
+
|
|
201
|
+
Available modules:
|
|
202
|
+
${moduleDescriptions}
|
|
203
|
+
|
|
204
|
+
Respond with JSON only: { "moduleId": "<module_id>", "confidence": <0.0-1.0> }
|
|
205
|
+
If no module matches well, use confidence < 0.3.
|
|
206
|
+
${historyContext}
|
|
207
|
+
User message: "${message}"`;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Parse a JSON response from the LLM, tolerating markdown code blocks
|
|
211
|
+
* and other wrapper text.
|
|
212
|
+
*/
|
|
213
|
+
function parseJsonResponse(text) {
|
|
214
|
+
const validate = (obj) => {
|
|
215
|
+
if (typeof obj === 'object' && obj !== null &&
|
|
216
|
+
typeof obj.moduleId === 'string' &&
|
|
217
|
+
typeof obj.confidence === 'number') {
|
|
218
|
+
return obj;
|
|
219
|
+
}
|
|
220
|
+
return null;
|
|
221
|
+
};
|
|
222
|
+
// Strip markdown fences before attempting parse
|
|
223
|
+
const cleaned = text.replace(/```(?:json)?\s*/g, '').replace(/```/g, '').trim();
|
|
224
|
+
try {
|
|
225
|
+
return validate(JSON.parse(cleaned));
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
// Try extracting any JSON object containing both required fields
|
|
229
|
+
const jsonMatch = cleaned.match(/\{[^}]*"(?:moduleId|confidence)"[^}]*\}/);
|
|
230
|
+
if (jsonMatch) {
|
|
231
|
+
try {
|
|
232
|
+
return validate(JSON.parse(jsonMatch[0]));
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
// Give up
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
// ============================================================================
|
|
242
|
+
// Factory
|
|
243
|
+
// ============================================================================
|
|
244
|
+
/**
|
|
245
|
+
* Create a local intent classifier that uses the user's own model for
|
|
246
|
+
* LLM classification, falling back to a multi-signal heuristic when
|
|
247
|
+
* the LLM is unavailable or fails.
|
|
248
|
+
*
|
|
249
|
+
* Heuristics use Snowball stemming (24 languages), prefix overlap, and
|
|
250
|
+
* character trigrams for language-agnostic fuzzy matching.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```ts
|
|
254
|
+
* import { generateText } from 'ai';
|
|
255
|
+
* import { openai } from '@ai-sdk/openai';
|
|
256
|
+
*
|
|
257
|
+
* const classifier = createIntentClassifier({
|
|
258
|
+
* modules: [taskModule, calendarModule],
|
|
259
|
+
* model: openai('gpt-4o-mini'),
|
|
260
|
+
* generateText,
|
|
261
|
+
* locale: 'de',
|
|
262
|
+
* });
|
|
263
|
+
*
|
|
264
|
+
* const result = await classifier.classify('create a new task');
|
|
265
|
+
* // { intent: 'tasks', confidence: 0.95 }
|
|
266
|
+
* ```
|
|
267
|
+
*/
|
|
268
|
+
function createIntentClassifier(config) {
|
|
269
|
+
const { modules, model, generateText, timeoutMs = 2000, confidenceThreshold = 0.5, minTokens = 2, locale, } = config;
|
|
270
|
+
return {
|
|
271
|
+
async classify(input, _context, conversationHistory) {
|
|
272
|
+
// Short-circuit: not enough tokens to classify meaningfully
|
|
273
|
+
const tokens = tokenize(input);
|
|
274
|
+
if (tokens.size < minTokens || modules.length === 0) {
|
|
275
|
+
return { intent: 'ambiguous', confidence: 0 };
|
|
276
|
+
}
|
|
277
|
+
// Try LLM classification when both model and generateText are available
|
|
278
|
+
if (model && generateText) {
|
|
279
|
+
try {
|
|
280
|
+
const result = await classifyWithLlm(input, modules, model, generateText, timeoutMs, conversationHistory);
|
|
281
|
+
if (result.confidence >= confidenceThreshold) {
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
284
|
+
// LLM returned low confidence — fall through to heuristics
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
// LLM failed — fall through to heuristics
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return classifyWithHeuristics(input, modules, locale);
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
// ============================================================================
|
|
295
|
+
// Internal LLM Classification
|
|
296
|
+
// ============================================================================
|
|
297
|
+
async function classifyWithLlm(input, modules, model, generateText, timeoutMs, conversationHistory) {
|
|
298
|
+
const prompt = buildClassificationPrompt(input, modules, conversationHistory);
|
|
299
|
+
const controller = new AbortController();
|
|
300
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
301
|
+
let result;
|
|
302
|
+
try {
|
|
303
|
+
result = await generateText({ model, prompt, abortSignal: controller.signal });
|
|
304
|
+
}
|
|
305
|
+
finally {
|
|
306
|
+
clearTimeout(timer);
|
|
307
|
+
}
|
|
308
|
+
const parsed = parseJsonResponse(result.text);
|
|
309
|
+
if (!parsed) {
|
|
310
|
+
throw new Error('Failed to parse LLM JSON response');
|
|
311
|
+
}
|
|
312
|
+
// Validate moduleId exists in configured modules
|
|
313
|
+
const matchedModule = modules.find((m) => m.id === parsed.moduleId);
|
|
314
|
+
if (!matchedModule) {
|
|
315
|
+
return {
|
|
316
|
+
intent: 'ambiguous',
|
|
317
|
+
confidence: Math.max(0, Math.min(1, parsed.confidence)),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
intent: parsed.moduleId,
|
|
322
|
+
confidence: Math.max(0, Math.min(1, parsed.confidence)),
|
|
323
|
+
};
|
|
324
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ModelSelectorConfig, ModelSelector, ModuleDefinition } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Create a model selector from a flat tier→modelId map.
|
|
4
|
+
* The consuming app provides an `instanceFactory` that turns a modelId
|
|
5
|
+
* string into a ready-to-use LanguageModel instance.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createModelSelectorFromMap(models: Record<string, string>, instanceFactory: (modelId: string) => unknown, modules?: ModuleDefinition[]): ModelSelector;
|
|
8
|
+
/**
|
|
9
|
+
* Create a model selector that picks the appropriate model based on
|
|
10
|
+
* intent classification and module configuration.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createModelSelector(tiers: ModelSelectorConfig, modules?: ModuleDefinition[]): ModelSelector;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createModelSelectorFromMap = createModelSelectorFromMap;
|
|
4
|
+
exports.createModelSelector = createModelSelector;
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Flat-map Factory
|
|
7
|
+
// ============================================================================
|
|
8
|
+
/**
|
|
9
|
+
* Create a model selector from a flat tier→modelId map.
|
|
10
|
+
* The consuming app provides an `instanceFactory` that turns a modelId
|
|
11
|
+
* string into a ready-to-use LanguageModel instance.
|
|
12
|
+
*/
|
|
13
|
+
function createModelSelectorFromMap(models, instanceFactory, modules) {
|
|
14
|
+
const resolve = (tier) => models[tier] ?? models.standard ?? 'default';
|
|
15
|
+
const tiers = {
|
|
16
|
+
fast: { modelId: resolve('fast'), instance: instanceFactory(resolve('fast')) },
|
|
17
|
+
standard: { modelId: resolve('standard'), instance: instanceFactory(resolve('standard')) },
|
|
18
|
+
premium: { modelId: resolve('powerful'), instance: instanceFactory(resolve('powerful')) },
|
|
19
|
+
};
|
|
20
|
+
return createModelSelector(tiers, modules);
|
|
21
|
+
}
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Factory
|
|
24
|
+
// ============================================================================
|
|
25
|
+
/**
|
|
26
|
+
* Create a model selector that picks the appropriate model based on
|
|
27
|
+
* intent classification and module configuration.
|
|
28
|
+
*/
|
|
29
|
+
function createModelSelector(tiers, modules) {
|
|
30
|
+
const moduleMap = modules
|
|
31
|
+
? new Map(modules.map((m) => [m.id, m]))
|
|
32
|
+
: new Map();
|
|
33
|
+
return {
|
|
34
|
+
select(intent, _context) {
|
|
35
|
+
const module = moduleMap.get(intent.intent);
|
|
36
|
+
const tier = module?.executionDefaults?.modelTier ?? 'standard';
|
|
37
|
+
switch (tier) {
|
|
38
|
+
case 'fast':
|
|
39
|
+
return tiers.fast;
|
|
40
|
+
case 'powerful':
|
|
41
|
+
return tiers.premium;
|
|
42
|
+
case 'standard':
|
|
43
|
+
default:
|
|
44
|
+
return tiers.standard;
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ModuleDefinition, AiRouterConfig, AiRouter } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Define a module for intent-based tool routing.
|
|
4
|
+
*/
|
|
5
|
+
export declare function defineModule(config: ModuleDefinition): ModuleDefinition;
|
|
6
|
+
/**
|
|
7
|
+
* Create an AI router that selects tools based on classified intent.
|
|
8
|
+
*
|
|
9
|
+
* The router uses confidence-based expansion:
|
|
10
|
+
* - High confidence (>= 0.85): Only primary module's own tools
|
|
11
|
+
* - Medium confidence (0.7-0.85): Primary module tools + shared tools from all modules
|
|
12
|
+
* - Low confidence (< 0.7): All tools (ambiguous fallback)
|
|
13
|
+
*/
|
|
14
|
+
export declare function createAiRouter(config: AiRouterConfig): AiRouter;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineModule = defineModule;
|
|
4
|
+
exports.createAiRouter = createAiRouter;
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Default Confidence Thresholds
|
|
7
|
+
// ============================================================================
|
|
8
|
+
const DEFAULT_THRESHOLDS = {
|
|
9
|
+
high: 0.85,
|
|
10
|
+
medium: 0.7,
|
|
11
|
+
};
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Helpers
|
|
14
|
+
// ============================================================================
|
|
15
|
+
function getExpansionStrategy(confidence, thresholds) {
|
|
16
|
+
if (confidence >= thresholds.high)
|
|
17
|
+
return 'single';
|
|
18
|
+
if (confidence >= thresholds.medium)
|
|
19
|
+
return 'expanded';
|
|
20
|
+
return 'all';
|
|
21
|
+
}
|
|
22
|
+
function getAllTools(modules) {
|
|
23
|
+
const all = new Set();
|
|
24
|
+
for (const mod of modules) {
|
|
25
|
+
for (const tool of mod.tools)
|
|
26
|
+
all.add(tool);
|
|
27
|
+
if (mod.sharedTools) {
|
|
28
|
+
for (const tool of mod.sharedTools)
|
|
29
|
+
all.add(tool);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return [...all];
|
|
33
|
+
}
|
|
34
|
+
function unique(tools) {
|
|
35
|
+
return [...new Set(tools)];
|
|
36
|
+
}
|
|
37
|
+
// ============================================================================
|
|
38
|
+
// Module Definition Helper
|
|
39
|
+
// ============================================================================
|
|
40
|
+
/**
|
|
41
|
+
* Define a module for intent-based tool routing.
|
|
42
|
+
*/
|
|
43
|
+
function defineModule(config) {
|
|
44
|
+
return config;
|
|
45
|
+
}
|
|
46
|
+
// ============================================================================
|
|
47
|
+
// Router Factory
|
|
48
|
+
// ============================================================================
|
|
49
|
+
/**
|
|
50
|
+
* Create an AI router that selects tools based on classified intent.
|
|
51
|
+
*
|
|
52
|
+
* The router uses confidence-based expansion:
|
|
53
|
+
* - High confidence (>= 0.85): Only primary module's own tools
|
|
54
|
+
* - Medium confidence (0.7-0.85): Primary module tools + shared tools from all modules
|
|
55
|
+
* - Low confidence (< 0.7): All tools (ambiguous fallback)
|
|
56
|
+
*/
|
|
57
|
+
function createAiRouter(config) {
|
|
58
|
+
const { modules, relatedModules = {}, confidenceThresholds = {}, } = config;
|
|
59
|
+
const thresholds = {
|
|
60
|
+
high: confidenceThresholds.high ?? DEFAULT_THRESHOLDS.high,
|
|
61
|
+
medium: confidenceThresholds.medium ?? DEFAULT_THRESHOLDS.medium,
|
|
62
|
+
};
|
|
63
|
+
const moduleMap = new Map(modules.map((m) => [m.id, m]));
|
|
64
|
+
const allToolNames = getAllTools(modules);
|
|
65
|
+
return {
|
|
66
|
+
route(intent, _allToolNames) {
|
|
67
|
+
// Ambiguous: use all tools
|
|
68
|
+
if (intent.intent === 'ambiguous') {
|
|
69
|
+
return {
|
|
70
|
+
tools: unique(allToolNames),
|
|
71
|
+
module: null,
|
|
72
|
+
strategy: 'all',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const primaryModule = moduleMap.get(intent.intent);
|
|
76
|
+
if (!primaryModule) {
|
|
77
|
+
return {
|
|
78
|
+
tools: unique(allToolNames),
|
|
79
|
+
module: null,
|
|
80
|
+
strategy: 'all',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const strategy = getExpansionStrategy(intent.confidence, thresholds);
|
|
84
|
+
switch (strategy) {
|
|
85
|
+
case 'single': {
|
|
86
|
+
return {
|
|
87
|
+
tools: unique([...primaryModule.tools]),
|
|
88
|
+
module: primaryModule,
|
|
89
|
+
strategy: 'single',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
case 'expanded': {
|
|
93
|
+
const sharedFromAll = modules.flatMap((m) => m.sharedTools ?? []);
|
|
94
|
+
return {
|
|
95
|
+
tools: unique([...primaryModule.tools, ...sharedFromAll]),
|
|
96
|
+
module: primaryModule,
|
|
97
|
+
strategy: 'expanded',
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
case 'all':
|
|
101
|
+
default:
|
|
102
|
+
return {
|
|
103
|
+
tools: unique(allToolNames),
|
|
104
|
+
module: null,
|
|
105
|
+
strategy: 'all',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Convert a Zod schema to a compact TypeScript-like description string.
|
|
4
|
+
*
|
|
5
|
+
* Used to auto-generate "Returns: ..." suffixes for AI tool descriptions,
|
|
6
|
+
* so the LLM knows the response shape without bloating the system prompt.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* zodToCompactDescription(z.object({
|
|
10
|
+
* tasks: z.array(z.object({ id: z.string(), title: z.string() })),
|
|
11
|
+
* total: z.number(),
|
|
12
|
+
* }))
|
|
13
|
+
* // → '{ tasks: Array<{ id: string, title: string }>, total: number }'
|
|
14
|
+
*
|
|
15
|
+
* @param schema - Any Zod schema
|
|
16
|
+
* @param maxDepth - Maximum nesting depth before collapsing to "..." (default: 3)
|
|
17
|
+
*/
|
|
18
|
+
export declare function zodToCompactDescription(schema: z.ZodTypeAny, maxDepth?: number): string;
|