@miller-tech/uap 1.0.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/LICENSE +21 -0
- package/README.md +888 -0
- package/dist/analyzers/index.d.ts +3 -0
- package/dist/analyzers/index.d.ts.map +1 -0
- package/dist/analyzers/index.js +684 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/benchmarks/agents/naive-agent.d.ts +60 -0
- package/dist/benchmarks/agents/naive-agent.d.ts.map +1 -0
- package/dist/benchmarks/agents/naive-agent.js +144 -0
- package/dist/benchmarks/agents/naive-agent.js.map +1 -0
- package/dist/benchmarks/agents/uap-agent.d.ts +167 -0
- package/dist/benchmarks/agents/uap-agent.d.ts.map +1 -0
- package/dist/benchmarks/agents/uap-agent.js +437 -0
- package/dist/benchmarks/agents/uap-agent.js.map +1 -0
- package/dist/benchmarks/benchmark.d.ts +328 -0
- package/dist/benchmarks/benchmark.d.ts.map +1 -0
- package/dist/benchmarks/benchmark.js +112 -0
- package/dist/benchmarks/benchmark.js.map +1 -0
- package/dist/benchmarks/execution-verifier.d.ts +41 -0
- package/dist/benchmarks/execution-verifier.d.ts.map +1 -0
- package/dist/benchmarks/execution-verifier.js +340 -0
- package/dist/benchmarks/execution-verifier.js.map +1 -0
- package/dist/benchmarks/hierarchical-prompting.d.ts +37 -0
- package/dist/benchmarks/hierarchical-prompting.d.ts.map +1 -0
- package/dist/benchmarks/hierarchical-prompting.js +246 -0
- package/dist/benchmarks/hierarchical-prompting.js.map +1 -0
- package/dist/benchmarks/improved-benchmark.d.ts +89 -0
- package/dist/benchmarks/improved-benchmark.d.ts.map +1 -0
- package/dist/benchmarks/improved-benchmark.js +585 -0
- package/dist/benchmarks/improved-benchmark.js.map +1 -0
- package/dist/benchmarks/index.d.ts +11 -0
- package/dist/benchmarks/index.d.ts.map +1 -0
- package/dist/benchmarks/index.js +11 -0
- package/dist/benchmarks/index.js.map +1 -0
- package/dist/benchmarks/model-integration.d.ts +111 -0
- package/dist/benchmarks/model-integration.d.ts.map +1 -0
- package/dist/benchmarks/model-integration.js +904 -0
- package/dist/benchmarks/model-integration.js.map +1 -0
- package/dist/benchmarks/multi-turn-agent.d.ts +44 -0
- package/dist/benchmarks/multi-turn-agent.d.ts.map +1 -0
- package/dist/benchmarks/multi-turn-agent.js +254 -0
- package/dist/benchmarks/multi-turn-agent.js.map +1 -0
- package/dist/benchmarks/multi-turn-loop.d.ts +57 -0
- package/dist/benchmarks/multi-turn-loop.d.ts.map +1 -0
- package/dist/benchmarks/multi-turn-loop.js +167 -0
- package/dist/benchmarks/multi-turn-loop.js.map +1 -0
- package/dist/benchmarks/tasks.d.ts +19 -0
- package/dist/benchmarks/tasks.d.ts.map +1 -0
- package/dist/benchmarks/tasks.js +435 -0
- package/dist/benchmarks/tasks.js.map +1 -0
- package/dist/bin/cli.d.ts +3 -0
- package/dist/bin/cli.d.ts.map +1 -0
- package/dist/bin/cli.js +546 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/bin/llama-server-optimize.d.ts +18 -0
- package/dist/bin/llama-server-optimize.d.ts.map +1 -0
- package/dist/bin/llama-server-optimize.js +708 -0
- package/dist/bin/llama-server-optimize.js.map +1 -0
- package/dist/bin/policy.d.ts +3 -0
- package/dist/bin/policy.d.ts.map +1 -0
- package/dist/bin/policy.js +143 -0
- package/dist/bin/policy.js.map +1 -0
- package/dist/bin/tool-calls.d.ts +3 -0
- package/dist/bin/tool-calls.d.ts.map +1 -0
- package/dist/bin/tool-calls.js +4 -0
- package/dist/bin/tool-calls.js.map +1 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +2 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/web-browser.d.ts +30 -0
- package/dist/browser/web-browser.d.ts.map +1 -0
- package/dist/browser/web-browser.js +93 -0
- package/dist/browser/web-browser.js.map +1 -0
- package/dist/cli/agent.d.ts +20 -0
- package/dist/cli/agent.d.ts.map +1 -0
- package/dist/cli/agent.js +474 -0
- package/dist/cli/agent.js.map +1 -0
- package/dist/cli/analyze.d.ts +7 -0
- package/dist/cli/analyze.d.ts.map +1 -0
- package/dist/cli/analyze.js +103 -0
- package/dist/cli/analyze.js.map +1 -0
- package/dist/cli/completion-gates.d.ts +51 -0
- package/dist/cli/completion-gates.d.ts.map +1 -0
- package/dist/cli/completion-gates.js +201 -0
- package/dist/cli/completion-gates.js.map +1 -0
- package/dist/cli/compliance.d.ts +8 -0
- package/dist/cli/compliance.d.ts.map +1 -0
- package/dist/cli/compliance.js +509 -0
- package/dist/cli/compliance.js.map +1 -0
- package/dist/cli/coord.d.ts +7 -0
- package/dist/cli/coord.d.ts.map +1 -0
- package/dist/cli/coord.js +138 -0
- package/dist/cli/coord.js.map +1 -0
- package/dist/cli/dashboard.d.ts +21 -0
- package/dist/cli/dashboard.d.ts.map +1 -0
- package/dist/cli/dashboard.js +1508 -0
- package/dist/cli/dashboard.js.map +1 -0
- package/dist/cli/deploy.d.ts +19 -0
- package/dist/cli/deploy.d.ts.map +1 -0
- package/dist/cli/deploy.js +387 -0
- package/dist/cli/deploy.js.map +1 -0
- package/dist/cli/droids.d.ts +9 -0
- package/dist/cli/droids.d.ts.map +1 -0
- package/dist/cli/droids.js +227 -0
- package/dist/cli/droids.js.map +1 -0
- package/dist/cli/generate.d.ts +17 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +432 -0
- package/dist/cli/generate.js.map +1 -0
- package/dist/cli/hooks.d.ts +9 -0
- package/dist/cli/hooks.d.ts.map +1 -0
- package/dist/cli/hooks.js +464 -0
- package/dist/cli/hooks.js.map +1 -0
- package/dist/cli/init.d.ts +12 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +364 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/mcp-router.d.ts +16 -0
- package/dist/cli/mcp-router.d.ts.map +1 -0
- package/dist/cli/mcp-router.js +143 -0
- package/dist/cli/mcp-router.js.map +1 -0
- package/dist/cli/memory.d.ts +24 -0
- package/dist/cli/memory.d.ts.map +1 -0
- package/dist/cli/memory.js +885 -0
- package/dist/cli/memory.js.map +1 -0
- package/dist/cli/model.d.ts +15 -0
- package/dist/cli/model.d.ts.map +1 -0
- package/dist/cli/model.js +290 -0
- package/dist/cli/model.js.map +1 -0
- package/dist/cli/patterns.d.ts +26 -0
- package/dist/cli/patterns.d.ts.map +1 -0
- package/dist/cli/patterns.js +862 -0
- package/dist/cli/patterns.js.map +1 -0
- package/dist/cli/rtk-validation.d.ts +9 -0
- package/dist/cli/rtk-validation.d.ts.map +1 -0
- package/dist/cli/rtk-validation.js +9 -0
- package/dist/cli/rtk-validation.js.map +1 -0
- package/dist/cli/rtk.d.ts +34 -0
- package/dist/cli/rtk.d.ts.map +1 -0
- package/dist/cli/rtk.js +401 -0
- package/dist/cli/rtk.js.map +1 -0
- package/dist/cli/schema-diff.d.ts +7 -0
- package/dist/cli/schema-diff.d.ts.map +1 -0
- package/dist/cli/schema-diff.js +11 -0
- package/dist/cli/schema-diff.js.map +1 -0
- package/dist/cli/setup-mcp-router.d.ts +8 -0
- package/dist/cli/setup-mcp-router.d.ts.map +1 -0
- package/dist/cli/setup-mcp-router.js +163 -0
- package/dist/cli/setup-mcp-router.js.map +1 -0
- package/dist/cli/setup-wizard.d.ts +2 -0
- package/dist/cli/setup-wizard.d.ts.map +1 -0
- package/dist/cli/setup-wizard.js +806 -0
- package/dist/cli/setup-wizard.js.map +1 -0
- package/dist/cli/setup.d.ts +15 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +154 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/sync.d.ts +8 -0
- package/dist/cli/sync.d.ts.map +1 -0
- package/dist/cli/sync.js +395 -0
- package/dist/cli/sync.js.map +1 -0
- package/dist/cli/task.d.ts +33 -0
- package/dist/cli/task.d.ts.map +1 -0
- package/dist/cli/task.js +672 -0
- package/dist/cli/task.js.map +1 -0
- package/dist/cli/tool-calls.d.ts +20 -0
- package/dist/cli/tool-calls.d.ts.map +1 -0
- package/dist/cli/tool-calls.js +605 -0
- package/dist/cli/tool-calls.js.map +1 -0
- package/dist/cli/uap.d.ts +10 -0
- package/dist/cli/uap.d.ts.map +1 -0
- package/dist/cli/uap.js +398 -0
- package/dist/cli/uap.js.map +1 -0
- package/dist/cli/update.d.ts +10 -0
- package/dist/cli/update.d.ts.map +1 -0
- package/dist/cli/update.js +300 -0
- package/dist/cli/update.js.map +1 -0
- package/dist/cli/visualize.d.ts +77 -0
- package/dist/cli/visualize.d.ts.map +1 -0
- package/dist/cli/visualize.js +287 -0
- package/dist/cli/visualize.js.map +1 -0
- package/dist/cli/worktree.d.ts +9 -0
- package/dist/cli/worktree.d.ts.map +1 -0
- package/dist/cli/worktree.js +213 -0
- package/dist/cli/worktree.js.map +1 -0
- package/dist/coordination/adaptive-patterns.d.ts +65 -0
- package/dist/coordination/adaptive-patterns.d.ts.map +1 -0
- package/dist/coordination/adaptive-patterns.js +108 -0
- package/dist/coordination/adaptive-patterns.js.map +1 -0
- package/dist/coordination/auto-agent.d.ts +82 -0
- package/dist/coordination/auto-agent.d.ts.map +1 -0
- package/dist/coordination/auto-agent.js +145 -0
- package/dist/coordination/auto-agent.js.map +1 -0
- package/dist/coordination/capability-router.d.ts +79 -0
- package/dist/coordination/capability-router.d.ts.map +1 -0
- package/dist/coordination/capability-router.js +334 -0
- package/dist/coordination/capability-router.js.map +1 -0
- package/dist/coordination/database.d.ts +13 -0
- package/dist/coordination/database.d.ts.map +1 -0
- package/dist/coordination/database.js +136 -0
- package/dist/coordination/database.js.map +1 -0
- package/dist/coordination/deploy-batcher.d.ts +122 -0
- package/dist/coordination/deploy-batcher.d.ts.map +1 -0
- package/dist/coordination/deploy-batcher.js +718 -0
- package/dist/coordination/deploy-batcher.js.map +1 -0
- package/dist/coordination/droid-validator.d.ts +59 -0
- package/dist/coordination/droid-validator.d.ts.map +1 -0
- package/dist/coordination/droid-validator.js +142 -0
- package/dist/coordination/droid-validator.js.map +1 -0
- package/dist/coordination/index.d.ts +10 -0
- package/dist/coordination/index.d.ts.map +1 -0
- package/dist/coordination/index.js +10 -0
- package/dist/coordination/index.js.map +1 -0
- package/dist/coordination/pattern-router.d.ts +50 -0
- package/dist/coordination/pattern-router.d.ts.map +1 -0
- package/dist/coordination/pattern-router.js +118 -0
- package/dist/coordination/pattern-router.js.map +1 -0
- package/dist/coordination/service.d.ts +81 -0
- package/dist/coordination/service.d.ts.map +1 -0
- package/dist/coordination/service.js +619 -0
- package/dist/coordination/service.js.map +1 -0
- package/dist/coordination/worktree-enforcer.d.ts +22 -0
- package/dist/coordination/worktree-enforcer.d.ts.map +1 -0
- package/dist/coordination/worktree-enforcer.js +71 -0
- package/dist/coordination/worktree-enforcer.js.map +1 -0
- package/dist/generators/claude-md.d.ts +3 -0
- package/dist/generators/claude-md.d.ts.map +1 -0
- package/dist/generators/claude-md.js +1020 -0
- package/dist/generators/claude-md.js.map +1 -0
- package/dist/generators/template-loader.d.ts +105 -0
- package/dist/generators/template-loader.d.ts.map +1 -0
- package/dist/generators/template-loader.js +291 -0
- package/dist/generators/template-loader.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-router/config/parser.d.ts +9 -0
- package/dist/mcp-router/config/parser.d.ts.map +1 -0
- package/dist/mcp-router/config/parser.js +174 -0
- package/dist/mcp-router/config/parser.js.map +1 -0
- package/dist/mcp-router/executor/client.d.ts +31 -0
- package/dist/mcp-router/executor/client.d.ts.map +1 -0
- package/dist/mcp-router/executor/client.js +189 -0
- package/dist/mcp-router/executor/client.js.map +1 -0
- package/dist/mcp-router/index.d.ts +22 -0
- package/dist/mcp-router/index.d.ts.map +1 -0
- package/dist/mcp-router/index.js +18 -0
- package/dist/mcp-router/index.js.map +1 -0
- package/dist/mcp-router/output-compressor.d.ts +26 -0
- package/dist/mcp-router/output-compressor.d.ts.map +1 -0
- package/dist/mcp-router/output-compressor.js +236 -0
- package/dist/mcp-router/output-compressor.js.map +1 -0
- package/dist/mcp-router/search/fuzzy.d.ts +26 -0
- package/dist/mcp-router/search/fuzzy.d.ts.map +1 -0
- package/dist/mcp-router/search/fuzzy.js +94 -0
- package/dist/mcp-router/search/fuzzy.js.map +1 -0
- package/dist/mcp-router/server.d.ts +50 -0
- package/dist/mcp-router/server.d.ts.map +1 -0
- package/dist/mcp-router/server.js +229 -0
- package/dist/mcp-router/server.js.map +1 -0
- package/dist/mcp-router/session-stats.d.ts +37 -0
- package/dist/mcp-router/session-stats.d.ts.map +1 -0
- package/dist/mcp-router/session-stats.js +56 -0
- package/dist/mcp-router/session-stats.js.map +1 -0
- package/dist/mcp-router/tools/discover.d.ts +37 -0
- package/dist/mcp-router/tools/discover.d.ts.map +1 -0
- package/dist/mcp-router/tools/discover.js +65 -0
- package/dist/mcp-router/tools/discover.js.map +1 -0
- package/dist/mcp-router/tools/execute.d.ts +43 -0
- package/dist/mcp-router/tools/execute.d.ts.map +1 -0
- package/dist/mcp-router/tools/execute.js +144 -0
- package/dist/mcp-router/tools/execute.js.map +1 -0
- package/dist/mcp-router/types.d.ts +62 -0
- package/dist/mcp-router/types.d.ts.map +1 -0
- package/dist/mcp-router/types.js +6 -0
- package/dist/mcp-router/types.js.map +1 -0
- package/dist/memory/adaptive-context.d.ts +149 -0
- package/dist/memory/adaptive-context.d.ts.map +1 -0
- package/dist/memory/adaptive-context.js +1095 -0
- package/dist/memory/adaptive-context.js.map +1 -0
- package/dist/memory/agent-scoped-memory.d.ts +67 -0
- package/dist/memory/agent-scoped-memory.d.ts.map +1 -0
- package/dist/memory/agent-scoped-memory.js +126 -0
- package/dist/memory/agent-scoped-memory.js.map +1 -0
- package/dist/memory/ambiguity-detector.d.ts +54 -0
- package/dist/memory/ambiguity-detector.d.ts.map +1 -0
- package/dist/memory/ambiguity-detector.js +401 -0
- package/dist/memory/ambiguity-detector.js.map +1 -0
- package/dist/memory/backends/base.d.ts +18 -0
- package/dist/memory/backends/base.d.ts.map +1 -0
- package/dist/memory/backends/base.js +2 -0
- package/dist/memory/backends/base.js.map +1 -0
- package/dist/memory/backends/factory.d.ts +4 -0
- package/dist/memory/backends/factory.d.ts.map +1 -0
- package/dist/memory/backends/factory.js +53 -0
- package/dist/memory/backends/factory.js.map +1 -0
- package/dist/memory/backends/github.d.ts +27 -0
- package/dist/memory/backends/github.d.ts.map +1 -0
- package/dist/memory/backends/github.js +134 -0
- package/dist/memory/backends/github.js.map +1 -0
- package/dist/memory/backends/qdrant-cloud.d.ts +32 -0
- package/dist/memory/backends/qdrant-cloud.d.ts.map +1 -0
- package/dist/memory/backends/qdrant-cloud.js +167 -0
- package/dist/memory/backends/qdrant-cloud.js.map +1 -0
- package/dist/memory/context-compressor.d.ts +116 -0
- package/dist/memory/context-compressor.d.ts.map +1 -0
- package/dist/memory/context-compressor.js +430 -0
- package/dist/memory/context-compressor.js.map +1 -0
- package/dist/memory/context-pruner.d.ts +55 -0
- package/dist/memory/context-pruner.d.ts.map +1 -0
- package/dist/memory/context-pruner.js +85 -0
- package/dist/memory/context-pruner.js.map +1 -0
- package/dist/memory/correction-propagator.d.ts +44 -0
- package/dist/memory/correction-propagator.d.ts.map +1 -0
- package/dist/memory/correction-propagator.js +156 -0
- package/dist/memory/correction-propagator.js.map +1 -0
- package/dist/memory/daily-log.d.ts +67 -0
- package/dist/memory/daily-log.d.ts.map +1 -0
- package/dist/memory/daily-log.js +143 -0
- package/dist/memory/daily-log.js.map +1 -0
- package/dist/memory/dynamic-retrieval.d.ts +112 -0
- package/dist/memory/dynamic-retrieval.d.ts.map +1 -0
- package/dist/memory/dynamic-retrieval.js +908 -0
- package/dist/memory/dynamic-retrieval.js.map +1 -0
- package/dist/memory/embeddings.d.ts +172 -0
- package/dist/memory/embeddings.d.ts.map +1 -0
- package/dist/memory/embeddings.js +780 -0
- package/dist/memory/embeddings.js.map +1 -0
- package/dist/memory/generic-uap-patterns.d.ts +7 -0
- package/dist/memory/generic-uap-patterns.d.ts.map +1 -0
- package/dist/memory/generic-uap-patterns.js +43 -0
- package/dist/memory/generic-uap-patterns.js.map +1 -0
- package/dist/memory/hierarchical-memory.d.ts +141 -0
- package/dist/memory/hierarchical-memory.d.ts.map +1 -0
- package/dist/memory/hierarchical-memory.js +485 -0
- package/dist/memory/hierarchical-memory.js.map +1 -0
- package/dist/memory/knowledge-graph.d.ts +98 -0
- package/dist/memory/knowledge-graph.d.ts.map +1 -0
- package/dist/memory/knowledge-graph.js +275 -0
- package/dist/memory/knowledge-graph.js.map +1 -0
- package/dist/memory/memory-consolidator.d.ts +124 -0
- package/dist/memory/memory-consolidator.d.ts.map +1 -0
- package/dist/memory/memory-consolidator.js +514 -0
- package/dist/memory/memory-consolidator.js.map +1 -0
- package/dist/memory/memory-maintenance.d.ts +39 -0
- package/dist/memory/memory-maintenance.d.ts.map +1 -0
- package/dist/memory/memory-maintenance.js +336 -0
- package/dist/memory/memory-maintenance.js.map +1 -0
- package/dist/memory/model-router.d.ts +105 -0
- package/dist/memory/model-router.d.ts.map +1 -0
- package/dist/memory/model-router.js +474 -0
- package/dist/memory/model-router.js.map +1 -0
- package/dist/memory/multi-view-memory.d.ts +134 -0
- package/dist/memory/multi-view-memory.d.ts.map +1 -0
- package/dist/memory/multi-view-memory.js +430 -0
- package/dist/memory/multi-view-memory.js.map +1 -0
- package/dist/memory/predictive-memory.d.ts +79 -0
- package/dist/memory/predictive-memory.d.ts.map +1 -0
- package/dist/memory/predictive-memory.js +294 -0
- package/dist/memory/predictive-memory.js.map +1 -0
- package/dist/memory/prepopulate.d.ts +76 -0
- package/dist/memory/prepopulate.d.ts.map +1 -0
- package/dist/memory/prepopulate.js +832 -0
- package/dist/memory/prepopulate.js.map +1 -0
- package/dist/memory/semantic-compression.d.ts +77 -0
- package/dist/memory/semantic-compression.d.ts.map +1 -0
- package/dist/memory/semantic-compression.js +359 -0
- package/dist/memory/semantic-compression.js.map +1 -0
- package/dist/memory/serverless-qdrant.d.ts +102 -0
- package/dist/memory/serverless-qdrant.d.ts.map +1 -0
- package/dist/memory/serverless-qdrant.js +369 -0
- package/dist/memory/serverless-qdrant.js.map +1 -0
- package/dist/memory/short-term/factory.d.ts +26 -0
- package/dist/memory/short-term/factory.d.ts.map +1 -0
- package/dist/memory/short-term/factory.js +28 -0
- package/dist/memory/short-term/factory.js.map +1 -0
- package/dist/memory/short-term/indexeddb.d.ts +25 -0
- package/dist/memory/short-term/indexeddb.d.ts.map +1 -0
- package/dist/memory/short-term/indexeddb.js +64 -0
- package/dist/memory/short-term/indexeddb.js.map +1 -0
- package/dist/memory/short-term/schema.d.ts +6 -0
- package/dist/memory/short-term/schema.d.ts.map +1 -0
- package/dist/memory/short-term/schema.js +141 -0
- package/dist/memory/short-term/schema.js.map +1 -0
- package/dist/memory/short-term/sqlite.d.ts +64 -0
- package/dist/memory/short-term/sqlite.d.ts.map +1 -0
- package/dist/memory/short-term/sqlite.js +274 -0
- package/dist/memory/short-term/sqlite.js.map +1 -0
- package/dist/memory/speculative-cache.d.ts +111 -0
- package/dist/memory/speculative-cache.d.ts.map +1 -0
- package/dist/memory/speculative-cache.js +457 -0
- package/dist/memory/speculative-cache.js.map +1 -0
- package/dist/memory/task-classifier.d.ts +40 -0
- package/dist/memory/task-classifier.d.ts.map +1 -0
- package/dist/memory/task-classifier.js +342 -0
- package/dist/memory/task-classifier.js.map +1 -0
- package/dist/memory/terminal-bench-knowledge.d.ts +48 -0
- package/dist/memory/terminal-bench-knowledge.d.ts.map +1 -0
- package/dist/memory/terminal-bench-knowledge.js +622 -0
- package/dist/memory/terminal-bench-knowledge.js.map +1 -0
- package/dist/memory/write-gate.d.ts +39 -0
- package/dist/memory/write-gate.d.ts.map +1 -0
- package/dist/memory/write-gate.js +190 -0
- package/dist/memory/write-gate.js.map +1 -0
- package/dist/models/api-client.d.ts +46 -0
- package/dist/models/api-client.d.ts.map +1 -0
- package/dist/models/api-client.js +182 -0
- package/dist/models/api-client.js.map +1 -0
- package/dist/models/execution-profiles.d.ts +64 -0
- package/dist/models/execution-profiles.d.ts.map +1 -0
- package/dist/models/execution-profiles.js +403 -0
- package/dist/models/execution-profiles.js.map +1 -0
- package/dist/models/executor.d.ts +130 -0
- package/dist/models/executor.d.ts.map +1 -0
- package/dist/models/executor.js +382 -0
- package/dist/models/executor.js.map +1 -0
- package/dist/models/index.d.ts +19 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/models/index.js +23 -0
- package/dist/models/index.js.map +1 -0
- package/dist/models/plan-validator.d.ts +37 -0
- package/dist/models/plan-validator.d.ts.map +1 -0
- package/dist/models/plan-validator.js +179 -0
- package/dist/models/plan-validator.js.map +1 -0
- package/dist/models/planner.d.ts +73 -0
- package/dist/models/planner.d.ts.map +1 -0
- package/dist/models/planner.js +375 -0
- package/dist/models/planner.js.map +1 -0
- package/dist/models/router.d.ts +96 -0
- package/dist/models/router.d.ts.map +1 -0
- package/dist/models/router.js +523 -0
- package/dist/models/router.js.map +1 -0
- package/dist/models/types.d.ts +370 -0
- package/dist/models/types.d.ts.map +1 -0
- package/dist/models/types.js +232 -0
- package/dist/models/types.js.map +1 -0
- package/dist/models/unified-router.d.ts +152 -0
- package/dist/models/unified-router.d.ts.map +1 -0
- package/dist/models/unified-router.js +313 -0
- package/dist/models/unified-router.js.map +1 -0
- package/dist/policies/convert-policy-to-claude.d.ts +3 -0
- package/dist/policies/convert-policy-to-claude.d.ts.map +1 -0
- package/dist/policies/convert-policy-to-claude.js +87 -0
- package/dist/policies/convert-policy-to-claude.js.map +1 -0
- package/dist/policies/database-manager.d.ts +27 -0
- package/dist/policies/database-manager.d.ts.map +1 -0
- package/dist/policies/database-manager.js +198 -0
- package/dist/policies/database-manager.js.map +1 -0
- package/dist/policies/enforced-tool-router.d.ts +53 -0
- package/dist/policies/enforced-tool-router.d.ts.map +1 -0
- package/dist/policies/enforced-tool-router.js +80 -0
- package/dist/policies/enforced-tool-router.js.map +1 -0
- package/dist/policies/index.d.ts +10 -0
- package/dist/policies/index.d.ts.map +1 -0
- package/dist/policies/index.js +8 -0
- package/dist/policies/index.js.map +1 -0
- package/dist/policies/policy-gate.d.ts +59 -0
- package/dist/policies/policy-gate.d.ts.map +1 -0
- package/dist/policies/policy-gate.js +171 -0
- package/dist/policies/policy-gate.js.map +1 -0
- package/dist/policies/policy-memory.d.ts +18 -0
- package/dist/policies/policy-memory.d.ts.map +1 -0
- package/dist/policies/policy-memory.js +126 -0
- package/dist/policies/policy-memory.js.map +1 -0
- package/dist/policies/policy-tools.d.ts +11 -0
- package/dist/policies/policy-tools.d.ts.map +1 -0
- package/dist/policies/policy-tools.js +66 -0
- package/dist/policies/policy-tools.js.map +1 -0
- package/dist/policies/schemas/policy.d.ts +69 -0
- package/dist/policies/schemas/policy.d.ts.map +1 -0
- package/dist/policies/schemas/policy.js +31 -0
- package/dist/policies/schemas/policy.js.map +1 -0
- package/dist/tasks/coordination.d.ts +83 -0
- package/dist/tasks/coordination.d.ts.map +1 -0
- package/dist/tasks/coordination.js +291 -0
- package/dist/tasks/coordination.js.map +1 -0
- package/dist/tasks/database.d.ts +19 -0
- package/dist/tasks/database.d.ts.map +1 -0
- package/dist/tasks/database.js +149 -0
- package/dist/tasks/database.js.map +1 -0
- package/dist/tasks/decoder-gate.d.ts +64 -0
- package/dist/tasks/decoder-gate.d.ts.map +1 -0
- package/dist/tasks/decoder-gate.js +268 -0
- package/dist/tasks/decoder-gate.js.map +1 -0
- package/dist/tasks/index.d.ts +6 -0
- package/dist/tasks/index.d.ts.map +1 -0
- package/dist/tasks/index.js +6 -0
- package/dist/tasks/index.js.map +1 -0
- package/dist/tasks/service.d.ts +40 -0
- package/dist/tasks/service.d.ts.map +1 -0
- package/dist/tasks/service.js +671 -0
- package/dist/tasks/service.js.map +1 -0
- package/dist/tasks/types.d.ts +238 -0
- package/dist/tasks/types.d.ts.map +1 -0
- package/dist/tasks/types.js +74 -0
- package/dist/tasks/types.js.map +1 -0
- package/dist/telemetry/index.d.ts +2 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +2 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/session-telemetry.d.ts +56 -0
- package/dist/telemetry/session-telemetry.d.ts.map +1 -0
- package/dist/telemetry/session-telemetry.js +807 -0
- package/dist/telemetry/session-telemetry.js.map +1 -0
- package/dist/types/analysis.d.ts +82 -0
- package/dist/types/analysis.d.ts.map +1 -0
- package/dist/types/analysis.js +2 -0
- package/dist/types/analysis.js.map +1 -0
- package/dist/types/config.d.ts +3324 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +418 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/coordination.d.ts +240 -0
- package/dist/types/coordination.d.ts.map +1 -0
- package/dist/types/coordination.js +43 -0
- package/dist/types/coordination.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -0
- package/dist/types/index.js.map +1 -0
- package/dist/uap-droids-strict.d.ts +59 -0
- package/dist/uap-droids-strict.d.ts.map +1 -0
- package/dist/uap-droids-strict.js +200 -0
- package/dist/uap-droids-strict.js.map +1 -0
- package/dist/utils/config-manager.d.ts +30 -0
- package/dist/utils/config-manager.d.ts.map +1 -0
- package/dist/utils/config-manager.js +41 -0
- package/dist/utils/config-manager.js.map +1 -0
- package/dist/utils/fetch-with-retry.d.ts +5 -0
- package/dist/utils/fetch-with-retry.d.ts.map +1 -0
- package/dist/utils/fetch-with-retry.js +61 -0
- package/dist/utils/fetch-with-retry.js.map +1 -0
- package/dist/utils/merge-claude-md.d.ts +28 -0
- package/dist/utils/merge-claude-md.d.ts.map +1 -0
- package/dist/utils/merge-claude-md.js +342 -0
- package/dist/utils/merge-claude-md.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +58 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +100 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/string-similarity.d.ts +37 -0
- package/dist/utils/string-similarity.d.ts.map +1 -0
- package/dist/utils/string-similarity.js +114 -0
- package/dist/utils/string-similarity.js.map +1 -0
- package/dist/utils/validate-json.d.ts +51 -0
- package/dist/utils/validate-json.d.ts.map +1 -0
- package/dist/utils/validate-json.js +94 -0
- package/dist/utils/validate-json.js.map +1 -0
- package/docs/INDEX.md +66 -0
- package/docs/architecture/MULTI_MODEL.md +224 -0
- package/docs/architecture/SYSTEM_ANALYSIS.md +1117 -0
- package/docs/architecture/UAP_COMPLIANCE.md +217 -0
- package/docs/architecture/UAP_PROTOCOL.md +339 -0
- package/docs/architecture/UAP_STRICT_DROIDS.md +172 -0
- package/docs/archive/BALLS_MODE_SELF_ANALYSIS.md +260 -0
- package/docs/archive/FAILING_TASKS_SOLUTION_PLAN.md +668 -0
- package/docs/archive/JINJA2-SYSTEM-MESSAGE-FIX.md +209 -0
- package/docs/archive/NPM-PUBLISH-V0.9.1.md +240 -0
- package/docs/archive/OPTIMIZATION_OPTIONS.md +334 -0
- package/docs/archive/SETUP_IMPROVEMENTS.md +213 -0
- package/docs/archive/UAP_GENERIC_OPTIMIZATION_PLAN.md +270 -0
- package/docs/archive/UAP_V103_PATTERN_DESIGN.md +315 -0
- package/docs/archive/UAP_V104_COMPLIANCE_DESIGN.md +223 -0
- package/docs/archive/changelog/2026-03-10_uap-100-compliance.md +77 -0
- package/docs/archive/changelog/2026-03-10_uap-full-system-verification.md +109 -0
- package/docs/benchmarks/ACCURACY_ANALYSIS.md +471 -0
- package/docs/benchmarks/TOKEN_OPTIMIZATION.md +572 -0
- package/docs/benchmarks/VALIDATION_PLAN.md +568 -0
- package/docs/benchmarks/VALIDATION_RESULTS.md +161 -0
- package/docs/deployment/DEPLOYMENT.md +895 -0
- package/docs/deployment/DEPLOYMENT_STRATEGIES.md +518 -0
- package/docs/deployment/DEPLOY_BATCHER_ANALYSIS.md +856 -0
- package/docs/deployment/DEPLOY_BATCHING.md +273 -0
- package/docs/deployment/DEPLOY_BUCKETING_ANALYSIS.md +420 -0
- package/docs/deployment/QWEN35_LLAMA_CPP.md +265 -0
- package/docs/getting-started/INTEGRATION.md +449 -0
- package/docs/getting-started/OVERVIEW.md +344 -0
- package/docs/getting-started/SETUP.md +203 -0
- package/docs/integrations/MCP_ROUTER_SETUP.md +445 -0
- package/docs/integrations/RTK_INTEGRATION.md +468 -0
- package/docs/operations/TROUBLESHOOTING.md +660 -0
- package/docs/reference/API_REFERENCE.md +903 -0
- package/docs/reference/FEATURES.md +472 -0
- package/docs/reference/HARNESS-MATRIX.md +318 -0
- package/docs/reference/UAP_CLI_REFERENCE.md +600 -0
- package/docs/research/BEHAVIORAL_PATTERNS.md +228 -0
- package/docs/research/DOMAIN_STRATEGIES.md +316 -0
- package/docs/research/MEMORY_SYSTEMS_COMPARISON.md +812 -0
- package/docs/research/PATTERN_ANALYSIS_2026-01-18.md +436 -0
- package/docs/research/PERFORMANCE_ANALYSIS_2026-01-18.md +209 -0
- package/docs/research/PERFORMANCE_TEST_PLAN.md +383 -0
- package/docs/research/TERMINAL_BENCH_LEARNINGS.md +217 -0
- package/package.json +113 -0
- package/scripts/README.md +161 -0
- package/templates/CLAUDE.template.md +10 -0
- package/templates/CLAUDE_ARCHITECTURE.template.md +103 -0
- package/templates/CLAUDE_CODING.template.md +127 -0
- package/templates/CLAUDE_DROIDS.template.md +109 -0
- package/templates/CLAUDE_MEMORY.template.md +131 -0
- package/templates/CLAUDE_WORKFLOWS.template.md +139 -0
- package/templates/PROJECT.template.md +209 -0
- package/templates/SCHEMA.md +57 -0
- package/templates/archive/CLAUDE.template.root-v6.md +534 -0
- package/templates/archive/CLAUDE.template.v6.md +534 -0
- package/templates/hooks/forgecode/pre-compact.sh +68 -0
- package/templates/hooks/forgecode/session-start.sh +169 -0
- package/templates/hooks/forgecode.plugin.sh +128 -0
- package/templates/hooks/pre-compact.sh +74 -0
- package/templates/hooks/session-start.sh +366 -0
- package/tools/agents/README.md +224 -0
- package/tools/agents/UAP/README.md +386 -0
- package/tools/agents/UAP/__init__.py +9 -0
- package/tools/agents/UAP/cli.py +901 -0
- package/tools/agents/UAP/compliance_verify.sh +108 -0
- package/tools/agents/UAP/full_verification.sh +126 -0
- package/tools/agents/UAP/version.py +32 -0
- package/tools/agents/benchmarks/benchmark_memory_systems.py +730 -0
- package/tools/agents/benchmarks/results/benchmark_20260106_064817.json +170 -0
- package/tools/agents/benchmarks/results/benchmark_20260106_064817.md +51 -0
- package/tools/agents/config/chat_template.jinja +77 -0
- package/tools/agents/config/tool-call-schema.json +19 -0
- package/tools/agents/config/tool-call.gbnf +58 -0
- package/tools/agents/docker/Dockerfile.python +52 -0
- package/tools/agents/docker/Dockerfile.ubuntu +55 -0
- package/tools/agents/docker-compose.qdrant.yml +24 -0
- package/tools/agents/install-opencode-local.sh.j2 +135 -0
- package/tools/agents/migrations/apply.py +256 -0
- package/tools/agents/opencode_uap_agent.py +1505 -0
- package/tools/agents/plugin/README.md +91 -0
- package/tools/agents/plugin/index.ts +46 -0
- package/tools/agents/plugin/pre-compact.sh +68 -0
- package/tools/agents/plugin/session-start.sh +175 -0
- package/tools/agents/plugin/uap-commands.ts +45 -0
- package/tools/agents/plugin/uap-droids.ts +54 -0
- package/tools/agents/plugin/uap-patterns.ts +54 -0
- package/tools/agents/plugin/uap-skills.ts +52 -0
- package/tools/agents/plugins/uap-enforce.ts +314 -0
- package/tools/agents/scripts/__pycache__/tool_call_wrapper.cpython-313.pyc +0 -0
- package/tools/agents/scripts/chat_template_verifier.py +343 -0
- package/tools/agents/scripts/fix-qwen-template.js +38 -0
- package/tools/agents/scripts/fix_qwen_chat_template.py +316 -0
- package/tools/agents/scripts/generate_lora_training_data.py +412 -0
- package/tools/agents/scripts/init_qdrant.py +151 -0
- package/tools/agents/scripts/memory_migration.py +560 -0
- package/tools/agents/scripts/migrate_memory_to_qdrant.py +110 -0
- package/tools/agents/scripts/prepare_lora.sh +512 -0
- package/tools/agents/scripts/query_memory.py +200 -0
- package/tools/agents/scripts/qwen-tool-call-test.js +38 -0
- package/tools/agents/scripts/qwen-tool-call-wrapper.js +38 -0
- package/tools/agents/scripts/qwen_tool_call_test.py +464 -0
- package/tools/agents/scripts/qwen_tool_call_wrapper.py +686 -0
- package/tools/agents/scripts/start-services.sh +96 -0
- package/tools/agents/scripts/tool-choice-proxy.cjs +296 -0
- package/tools/agents/scripts/tool_call_test.py +656 -0
- package/tools/agents/scripts/tool_call_wrapper.py +799 -0
- package/tools/agents/tests/test_uap_compliance.py +257 -0
- package/tools/agents/uap_agent.py +122 -0
- package/tools/agents/uap_agent_install.sh +12 -0
|
@@ -0,0 +1,799 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
UAP Tool Call Wrapper with Retry Logic
|
|
4
|
+
|
|
5
|
+
Model-agnostic OpenAI-compatible tool calling client with automatic retry
|
|
6
|
+
on failures, addressing common issues with local and hosted models.
|
|
7
|
+
|
|
8
|
+
Strategies implemented:
|
|
9
|
+
1. tool_choice="required" + parallel_tool_calls=true in API requests
|
|
10
|
+
2. Improved multi-tool system prompt with explicit format guidance
|
|
11
|
+
3. Retry escalation: auto -> required -> required+lower temp
|
|
12
|
+
4. Per-tool tool_choice for single-tool scenarios
|
|
13
|
+
5. Thinking mode suppression via chat_template_kwargs
|
|
14
|
+
6. Dynamic temperature decay on retries
|
|
15
|
+
|
|
16
|
+
Model Profiles:
|
|
17
|
+
Set UAP_MODEL_PROFILE env var to load model-specific defaults.
|
|
18
|
+
Supported profiles: qwen35, llama, generic (default)
|
|
19
|
+
Or pass a custom config dict to override any setting.
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
from tool_call_wrapper import ToolCallClient
|
|
23
|
+
|
|
24
|
+
client = ToolCallClient()
|
|
25
|
+
response = client.chat_with_tools(
|
|
26
|
+
messages=[{"role": "user", "content": "Call read_file with path='/etc/hosts'"}],
|
|
27
|
+
tools=[...]
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# With explicit model profile
|
|
31
|
+
client = ToolCallClient(config={"model_profile": "qwen35"})
|
|
32
|
+
|
|
33
|
+
Backward Compatibility:
|
|
34
|
+
# Legacy names still work
|
|
35
|
+
from tool_call_wrapper import Qwen35ToolCallClient # alias for ToolCallClient
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
import openai
|
|
40
|
+
except ImportError:
|
|
41
|
+
raise ImportError(
|
|
42
|
+
"The 'openai' package is required but not installed.\n"
|
|
43
|
+
"Install it with: pip install openai\n"
|
|
44
|
+
"Or: pip install -r requirements.txt"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
import time
|
|
48
|
+
import json
|
|
49
|
+
import os
|
|
50
|
+
import logging
|
|
51
|
+
from typing import List, Dict, Any, Optional, Tuple
|
|
52
|
+
from dataclasses import dataclass, asdict
|
|
53
|
+
from datetime import datetime
|
|
54
|
+
from enum import Enum
|
|
55
|
+
from pathlib import Path
|
|
56
|
+
|
|
57
|
+
# Configure logging
|
|
58
|
+
logging.basicConfig(
|
|
59
|
+
level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
60
|
+
)
|
|
61
|
+
logger = logging.getLogger("uap_tool_call")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ── Model Profiles ──────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
MODEL_PROFILES: Dict[str, Dict[str, Any]] = {
|
|
67
|
+
"generic": {
|
|
68
|
+
"temperature": 0.6,
|
|
69
|
+
"top_p": 0.9,
|
|
70
|
+
"presence_penalty": 0.0,
|
|
71
|
+
"max_tokens": 4096,
|
|
72
|
+
"enable_thinking": False,
|
|
73
|
+
"model": "default",
|
|
74
|
+
"base_url": "http://127.0.0.1:8080/v1",
|
|
75
|
+
"api_key": "not-needed",
|
|
76
|
+
"default_tool_choice": "auto",
|
|
77
|
+
"parallel_tool_calls": True,
|
|
78
|
+
"batch_tool_calls": True,
|
|
79
|
+
"escalate_tool_choice": True,
|
|
80
|
+
"use_per_tool_choice": True,
|
|
81
|
+
"dynamic_temperature": True,
|
|
82
|
+
"dynamic_temp_decay": 0.5,
|
|
83
|
+
"dynamic_temp_floor": 0.2,
|
|
84
|
+
},
|
|
85
|
+
"qwen35": {
|
|
86
|
+
"temperature": 0.6,
|
|
87
|
+
"top_p": 0.9,
|
|
88
|
+
"presence_penalty": 0.0,
|
|
89
|
+
"max_tokens": 4096,
|
|
90
|
+
"enable_thinking": False,
|
|
91
|
+
"model": "qwen35-a3b-iq4xs",
|
|
92
|
+
"base_url": "http://127.0.0.1:8080/v1",
|
|
93
|
+
"api_key": "not-needed",
|
|
94
|
+
"default_tool_choice": "required",
|
|
95
|
+
"parallel_tool_calls": True,
|
|
96
|
+
"batch_tool_calls": True,
|
|
97
|
+
"escalate_tool_choice": True,
|
|
98
|
+
"use_per_tool_choice": True,
|
|
99
|
+
"dynamic_temperature": True,
|
|
100
|
+
"dynamic_temp_decay": 0.5,
|
|
101
|
+
"dynamic_temp_floor": 0.2,
|
|
102
|
+
# Qwen-specific: suppress thinking mode to avoid tag leakage
|
|
103
|
+
"suppress_thinking": True,
|
|
104
|
+
"batch_system_prompt": (
|
|
105
|
+
"CRITICAL: You MUST emit ALL tool calls in a SINGLE response. "
|
|
106
|
+
"Each tool call must be a separate function call. "
|
|
107
|
+
"Do NOT call one tool and wait - emit ALL tool calls together NOW. "
|
|
108
|
+
"If asked to do 3 things, you must produce 3 tool calls in one response."
|
|
109
|
+
),
|
|
110
|
+
},
|
|
111
|
+
"llama": {
|
|
112
|
+
"temperature": 0.6,
|
|
113
|
+
"top_p": 0.9,
|
|
114
|
+
"presence_penalty": 0.0,
|
|
115
|
+
"max_tokens": 4096,
|
|
116
|
+
"enable_thinking": False,
|
|
117
|
+
"model": "default",
|
|
118
|
+
"base_url": "http://127.0.0.1:8080/v1",
|
|
119
|
+
"api_key": "not-needed",
|
|
120
|
+
"default_tool_choice": "auto",
|
|
121
|
+
"parallel_tool_calls": True,
|
|
122
|
+
"batch_tool_calls": True,
|
|
123
|
+
"escalate_tool_choice": True,
|
|
124
|
+
"use_per_tool_choice": True,
|
|
125
|
+
"dynamic_temperature": True,
|
|
126
|
+
"dynamic_temp_decay": 0.5,
|
|
127
|
+
"dynamic_temp_floor": 0.2,
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# Default batch system prompt (used when batch_tool_calls is True and no profile-specific one)
|
|
132
|
+
DEFAULT_BATCH_SYSTEM_PROMPT = (
|
|
133
|
+
"When multiple tools are needed, call ALL of them in a single response. "
|
|
134
|
+
"Do not call one tool and wait for a response before calling the next."
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _load_profile(profile_name: str) -> Dict[str, Any]:
|
|
139
|
+
"""Load a model profile by name, falling back to generic."""
|
|
140
|
+
base = MODEL_PROFILES.get("generic", {}).copy()
|
|
141
|
+
profile = MODEL_PROFILES.get(profile_name, {})
|
|
142
|
+
base.update(profile)
|
|
143
|
+
return base
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _detect_profile() -> str:
|
|
147
|
+
"""Auto-detect model profile from environment."""
|
|
148
|
+
return os.environ.get("UAP_MODEL_PROFILE", "generic")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ── Core Classes ─────────────────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class ToolCallStatus(Enum):
|
|
155
|
+
"""Status of tool call attempts"""
|
|
156
|
+
|
|
157
|
+
SUCCESS = "success"
|
|
158
|
+
FAILURE = "failure"
|
|
159
|
+
RETRY = "retry"
|
|
160
|
+
MAX_RETRIES = "max_retries"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@dataclass
|
|
164
|
+
class ToolCallMetrics:
|
|
165
|
+
"""Metrics for tool call performance"""
|
|
166
|
+
|
|
167
|
+
total_attempts: int = 0
|
|
168
|
+
successful_calls: int = 0
|
|
169
|
+
failed_calls: int = 0
|
|
170
|
+
retries: int = 0
|
|
171
|
+
avg_latency_ms: float = 0.0
|
|
172
|
+
last_error: Optional[str] = None
|
|
173
|
+
tool_choice_escalations: int = 0
|
|
174
|
+
parallel_calls_requested: int = 0
|
|
175
|
+
parallel_calls_received: int = 0
|
|
176
|
+
|
|
177
|
+
def to_dict(self) -> Dict:
|
|
178
|
+
return asdict(self)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class ToolCallError(Exception):
|
|
182
|
+
"""Custom exception for tool call failures"""
|
|
183
|
+
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class ToolCallClient:
|
|
188
|
+
"""
|
|
189
|
+
OpenAI-compatible client for reliable tool calling with any model.
|
|
190
|
+
|
|
191
|
+
Implements 6 strategies for maximum tool call reliability:
|
|
192
|
+
1. tool_choice + parallel_tool_calls in every request
|
|
193
|
+
2. Explicit multi-tool system prompt with format guidance
|
|
194
|
+
3. Retry escalation: auto -> required -> required+lower temp
|
|
195
|
+
4. Per-tool tool_choice for single-tool scenarios
|
|
196
|
+
5. Thinking mode suppression (model-specific)
|
|
197
|
+
6. Dynamic temperature decay on retries
|
|
198
|
+
|
|
199
|
+
Supports model profiles for model-specific tuning.
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
DEFAULT_CONFIG = {
|
|
203
|
+
"temperature": 0.6,
|
|
204
|
+
"top_p": 0.9,
|
|
205
|
+
"presence_penalty": 0.0,
|
|
206
|
+
"max_tokens": 4096,
|
|
207
|
+
"enable_thinking": False,
|
|
208
|
+
"max_retries": 3,
|
|
209
|
+
"backoff_factor": 2.0,
|
|
210
|
+
"base_url": "http://127.0.0.1:8080/v1",
|
|
211
|
+
"api_key": "not-needed",
|
|
212
|
+
"model": "default",
|
|
213
|
+
"default_tool_choice": "auto",
|
|
214
|
+
"parallel_tool_calls": True,
|
|
215
|
+
"batch_tool_calls": True,
|
|
216
|
+
"batch_system_prompt": DEFAULT_BATCH_SYSTEM_PROMPT,
|
|
217
|
+
"escalate_tool_choice": True,
|
|
218
|
+
"use_per_tool_choice": True,
|
|
219
|
+
"dynamic_temperature": True,
|
|
220
|
+
"dynamic_temp_decay": 0.5,
|
|
221
|
+
"dynamic_temp_floor": 0.2,
|
|
222
|
+
"suppress_thinking": False,
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
def __init__(
|
|
226
|
+
self, config: Optional[Dict[str, Any]] = None, enable_metrics: bool = True
|
|
227
|
+
):
|
|
228
|
+
"""
|
|
229
|
+
Initialize tool call client.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
config: Override default configuration. Can include "model_profile"
|
|
233
|
+
key to load a named profile as the base.
|
|
234
|
+
enable_metrics: Enable performance metrics tracking
|
|
235
|
+
"""
|
|
236
|
+
# Determine base config from profile
|
|
237
|
+
user_config = config or {}
|
|
238
|
+
profile_name = user_config.pop("model_profile", None) or _detect_profile()
|
|
239
|
+
profile_config = _load_profile(profile_name)
|
|
240
|
+
|
|
241
|
+
# Layer: defaults < profile < user overrides
|
|
242
|
+
self.config = {**self.DEFAULT_CONFIG, **profile_config, **user_config}
|
|
243
|
+
self.profile_name = profile_name
|
|
244
|
+
self.enable_metrics = enable_metrics
|
|
245
|
+
self.metrics = ToolCallMetrics()
|
|
246
|
+
self._client = None
|
|
247
|
+
|
|
248
|
+
# Initialize OpenAI client
|
|
249
|
+
self._init_client()
|
|
250
|
+
|
|
251
|
+
logger.info(
|
|
252
|
+
f"ToolCallClient initialized (profile: {profile_name}, "
|
|
253
|
+
f"model: {self.config['model']}, base_url: {self.config['base_url']})"
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
def _init_client(self):
|
|
257
|
+
"""Initialize OpenAI-compatible client"""
|
|
258
|
+
try:
|
|
259
|
+
self._client = openai.Client(
|
|
260
|
+
base_url=self.config["base_url"], api_key=self.config["api_key"]
|
|
261
|
+
)
|
|
262
|
+
logger.info(f"Connected to {self.config['base_url']}")
|
|
263
|
+
except Exception as e:
|
|
264
|
+
logger.error(f"Failed to initialize OpenAI client: {e}")
|
|
265
|
+
raise ToolCallError(f"Client initialization failed: {e}")
|
|
266
|
+
|
|
267
|
+
def _get_tool_choice(
|
|
268
|
+
self,
|
|
269
|
+
tools: List[Dict[str, Any]],
|
|
270
|
+
attempt: int,
|
|
271
|
+
expected_tool: Optional[str] = None,
|
|
272
|
+
) -> Any:
|
|
273
|
+
"""
|
|
274
|
+
Determine tool_choice based on strategy and attempt number.
|
|
275
|
+
|
|
276
|
+
Strategy 3 (escalation):
|
|
277
|
+
- Attempt 0: use default_tool_choice (usually "auto")
|
|
278
|
+
- Attempt 1+: escalate to "required"
|
|
279
|
+
|
|
280
|
+
Strategy 5 (per-tool):
|
|
281
|
+
- If expected_tool is set and only one tool matches, use per-tool choice
|
|
282
|
+
"""
|
|
283
|
+
# Strategy 5: Per-tool choice for single expected tool
|
|
284
|
+
if expected_tool and self.config.get("use_per_tool_choice") and attempt == 0:
|
|
285
|
+
for tool in tools:
|
|
286
|
+
func = tool.get("function", {})
|
|
287
|
+
if func.get("name") == expected_tool:
|
|
288
|
+
logger.debug(f"Using per-tool choice: {expected_tool}")
|
|
289
|
+
return {
|
|
290
|
+
"type": "function",
|
|
291
|
+
"function": {"name": expected_tool},
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
# Strategy 3: Escalation on retries
|
|
295
|
+
if attempt > 0 and self.config.get("escalate_tool_choice"):
|
|
296
|
+
self.metrics.tool_choice_escalations += 1
|
|
297
|
+
logger.info(f"Escalating tool_choice to 'required' (attempt {attempt + 1})")
|
|
298
|
+
return "required"
|
|
299
|
+
|
|
300
|
+
return self.config.get("default_tool_choice", "auto")
|
|
301
|
+
|
|
302
|
+
def chat_with_tools(
|
|
303
|
+
self,
|
|
304
|
+
messages: List[Dict[str, str]],
|
|
305
|
+
tools: List[Dict[str, Any]],
|
|
306
|
+
max_retries: Optional[int] = None,
|
|
307
|
+
timeout: int = 120,
|
|
308
|
+
expected_tool: Optional[str] = None,
|
|
309
|
+
expected_tool_calls: Optional[int] = None,
|
|
310
|
+
**kwargs,
|
|
311
|
+
) -> openai.types.chat.ChatCompletion:
|
|
312
|
+
"""
|
|
313
|
+
Chat completion with automatic retry on tool call failures.
|
|
314
|
+
|
|
315
|
+
Implements all 6 strategies for reliable tool calling.
|
|
316
|
+
|
|
317
|
+
Args:
|
|
318
|
+
messages: List of chat messages
|
|
319
|
+
tools: List of tool definitions
|
|
320
|
+
max_retries: Override default max retries
|
|
321
|
+
timeout: Request timeout in seconds
|
|
322
|
+
expected_tool: If set, use per-tool tool_choice (Strategy 5)
|
|
323
|
+
expected_tool_calls: Expected number of tool calls (for validation)
|
|
324
|
+
**kwargs: Additional parameters passed to OpenAI API
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
ChatCompletion response with tool calls
|
|
328
|
+
|
|
329
|
+
Raises:
|
|
330
|
+
ToolCallError: After max retries exhausted
|
|
331
|
+
"""
|
|
332
|
+
max_retries = max_retries or self.config["max_retries"]
|
|
333
|
+
|
|
334
|
+
# Track timing
|
|
335
|
+
start_time = time.time()
|
|
336
|
+
|
|
337
|
+
# Make a copy of messages to avoid modifying original
|
|
338
|
+
current_messages = [msg.copy() for msg in messages]
|
|
339
|
+
|
|
340
|
+
# Strategy 2: Inject multi-tool system prompt
|
|
341
|
+
if self.config.get("batch_tool_calls") and len(tools) > 1:
|
|
342
|
+
batch_prompt = self.config.get(
|
|
343
|
+
"batch_system_prompt", DEFAULT_BATCH_SYSTEM_PROMPT
|
|
344
|
+
)
|
|
345
|
+
if batch_prompt:
|
|
346
|
+
has_system = any(m.get("role") == "system" for m in current_messages)
|
|
347
|
+
if has_system:
|
|
348
|
+
for m in current_messages:
|
|
349
|
+
if m.get("role") == "system":
|
|
350
|
+
m["content"] = m["content"] + "\n\n" + batch_prompt
|
|
351
|
+
break
|
|
352
|
+
else:
|
|
353
|
+
current_messages.insert(
|
|
354
|
+
0,
|
|
355
|
+
{"role": "system", "content": batch_prompt},
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
# Track parallel call expectations
|
|
359
|
+
if expected_tool_calls and expected_tool_calls > 1:
|
|
360
|
+
self.metrics.parallel_calls_requested += 1
|
|
361
|
+
|
|
362
|
+
# Base temperature for dynamic adjustment
|
|
363
|
+
base_temperature = self.config["temperature"]
|
|
364
|
+
|
|
365
|
+
for attempt in range(max_retries):
|
|
366
|
+
self.metrics.total_attempts += 1
|
|
367
|
+
|
|
368
|
+
# Strategy 6: Dynamic temperature - reduce on retries
|
|
369
|
+
if self.config.get("dynamic_temperature") and attempt > 0:
|
|
370
|
+
decay = self.config.get("dynamic_temp_decay", 0.5)
|
|
371
|
+
floor = self.config.get("dynamic_temp_floor", 0.2)
|
|
372
|
+
current_temp = max(floor, base_temperature * (decay**attempt))
|
|
373
|
+
logger.info(
|
|
374
|
+
f"Dynamic temperature: {current_temp:.2f} (attempt {attempt + 1})"
|
|
375
|
+
)
|
|
376
|
+
else:
|
|
377
|
+
current_temp = base_temperature
|
|
378
|
+
|
|
379
|
+
# Strategy 1 + 3 + 5: Determine tool_choice
|
|
380
|
+
tool_choice = self._get_tool_choice(tools, attempt, expected_tool)
|
|
381
|
+
|
|
382
|
+
try:
|
|
383
|
+
# Build request with all strategies applied
|
|
384
|
+
request_params = {
|
|
385
|
+
"model": self.config["model"],
|
|
386
|
+
"messages": current_messages,
|
|
387
|
+
"tools": tools,
|
|
388
|
+
"tool_choice": tool_choice,
|
|
389
|
+
"parallel_tool_calls": self.config.get("parallel_tool_calls", True),
|
|
390
|
+
"temperature": current_temp,
|
|
391
|
+
"top_p": self.config["top_p"],
|
|
392
|
+
"presence_penalty": self.config["presence_penalty"],
|
|
393
|
+
"max_tokens": self.config["max_tokens"],
|
|
394
|
+
"timeout": timeout,
|
|
395
|
+
**kwargs,
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
# Strategy 5: Thinking mode suppression (model-specific)
|
|
399
|
+
if self.config.get("suppress_thinking"):
|
|
400
|
+
request_params["extra_body"] = {
|
|
401
|
+
"chat_template_kwargs": {
|
|
402
|
+
"enable_thinking": self.config.get("enable_thinking", False)
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
logger.debug(
|
|
407
|
+
f"Attempt {attempt + 1}/{max_retries}: "
|
|
408
|
+
f"tool_choice={tool_choice}, temp={current_temp:.2f}, "
|
|
409
|
+
f"parallel={self.config.get('parallel_tool_calls', True)}"
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
# Make API call
|
|
413
|
+
response = self._client.chat.completions.create(**request_params)
|
|
414
|
+
|
|
415
|
+
# Validate response
|
|
416
|
+
tool_calls = response.choices[0].message.tool_calls
|
|
417
|
+
|
|
418
|
+
if self._validate_tool_call(tool_calls):
|
|
419
|
+
# Success!
|
|
420
|
+
self.metrics.successful_calls += 1
|
|
421
|
+
|
|
422
|
+
# Track parallel call success
|
|
423
|
+
if tool_calls and len(tool_calls) > 1:
|
|
424
|
+
self.metrics.parallel_calls_received += 1
|
|
425
|
+
|
|
426
|
+
# Calculate latency
|
|
427
|
+
latency_ms = (time.time() - start_time) * 1000
|
|
428
|
+
self.metrics.avg_latency_ms = latency_ms
|
|
429
|
+
|
|
430
|
+
logger.info(
|
|
431
|
+
f"Tool call successful after {attempt + 1} attempt(s): "
|
|
432
|
+
f"{len(tool_calls)} call(s)"
|
|
433
|
+
)
|
|
434
|
+
return response
|
|
435
|
+
else:
|
|
436
|
+
# Invalid format, retry with correction
|
|
437
|
+
logger.warning(f"Invalid tool call format on attempt {attempt + 1}")
|
|
438
|
+
current_messages = self._correct_prompt(current_messages, response)
|
|
439
|
+
self.metrics.retries += 1
|
|
440
|
+
|
|
441
|
+
if attempt < max_retries - 1:
|
|
442
|
+
# Exponential backoff
|
|
443
|
+
backoff = self.config["backoff_factor"] ** attempt
|
|
444
|
+
time.sleep(backoff)
|
|
445
|
+
logger.info(f"Retrying in {backoff:.1f}s...")
|
|
446
|
+
|
|
447
|
+
except Exception as e:
|
|
448
|
+
self.metrics.last_error = str(e)
|
|
449
|
+
logger.error(f"Error on attempt {attempt + 1}: {e}")
|
|
450
|
+
|
|
451
|
+
if attempt == max_retries - 1:
|
|
452
|
+
# Last attempt failed
|
|
453
|
+
self.metrics.failed_calls += 1
|
|
454
|
+
raise ToolCallError(
|
|
455
|
+
f"Failed after {max_retries} attempts: {str(e)}"
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
# Retry with backoff
|
|
459
|
+
backoff = self.config["backoff_factor"] ** attempt
|
|
460
|
+
time.sleep(backoff)
|
|
461
|
+
|
|
462
|
+
# Should not reach here, but just in case
|
|
463
|
+
self.metrics.failed_calls += 1
|
|
464
|
+
raise ToolCallError("Max retries exceeded")
|
|
465
|
+
|
|
466
|
+
def _validate_tool_call(self, tool_calls) -> bool:
|
|
467
|
+
"""
|
|
468
|
+
Validate that tool call has correct format.
|
|
469
|
+
|
|
470
|
+
Checks for:
|
|
471
|
+
- Non-empty tool calls list
|
|
472
|
+
- Valid function name
|
|
473
|
+
- Parseable JSON arguments
|
|
474
|
+
- No reasoning content leakage (<thinking>/<think> tags in arguments)
|
|
475
|
+
"""
|
|
476
|
+
if not tool_calls:
|
|
477
|
+
logger.debug("No tool calls returned")
|
|
478
|
+
return False
|
|
479
|
+
|
|
480
|
+
for tool_call in tool_calls:
|
|
481
|
+
# Check function name exists
|
|
482
|
+
if not tool_call.function or not tool_call.function.name:
|
|
483
|
+
logger.debug("Tool call missing function name")
|
|
484
|
+
return False
|
|
485
|
+
|
|
486
|
+
# Check arguments are present
|
|
487
|
+
arguments = tool_call.function.arguments
|
|
488
|
+
if not arguments:
|
|
489
|
+
logger.debug("Tool call missing arguments")
|
|
490
|
+
return False
|
|
491
|
+
|
|
492
|
+
# Check for thinking/reasoning tag leakage in arguments
|
|
493
|
+
# This affects models with reasoning modes (Qwen3, DeepSeek, etc.)
|
|
494
|
+
for tag in ["<thinking>", "</thinking>", "<think>", "</think>"]:
|
|
495
|
+
if tag in arguments:
|
|
496
|
+
logger.debug(f"Reasoning tag leakage detected: {tag}")
|
|
497
|
+
return False
|
|
498
|
+
|
|
499
|
+
# Validate arguments are parseable JSON
|
|
500
|
+
try:
|
|
501
|
+
parsed = json.loads(arguments)
|
|
502
|
+
if not isinstance(parsed, dict):
|
|
503
|
+
logger.debug(f"Arguments not a JSON object: {type(parsed)}")
|
|
504
|
+
return False
|
|
505
|
+
except (json.JSONDecodeError, TypeError) as e:
|
|
506
|
+
logger.debug(f"Arguments not valid JSON: {e}")
|
|
507
|
+
return False
|
|
508
|
+
|
|
509
|
+
return True
|
|
510
|
+
|
|
511
|
+
def _correct_prompt(
|
|
512
|
+
self, messages: List[Dict], response: openai.types.chat.ChatCompletion
|
|
513
|
+
) -> List[Dict]:
|
|
514
|
+
"""
|
|
515
|
+
Correct prompt after invalid tool call.
|
|
516
|
+
|
|
517
|
+
Appends correction message to guide model toward proper format.
|
|
518
|
+
"""
|
|
519
|
+
invalid_content = response.choices[0].message.content or ""
|
|
520
|
+
|
|
521
|
+
# Add assistant's failed attempt
|
|
522
|
+
messages.append(
|
|
523
|
+
{
|
|
524
|
+
"role": "assistant",
|
|
525
|
+
"content": f"I attempted a tool call but the format was invalid: "
|
|
526
|
+
f"{invalid_content[:200]}",
|
|
527
|
+
}
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
# Add user correction
|
|
531
|
+
messages.append(
|
|
532
|
+
{
|
|
533
|
+
"role": "user",
|
|
534
|
+
"content": "Please call the tool using the correct format. "
|
|
535
|
+
"You must use the tool calling interface - respond with a tool call, "
|
|
536
|
+
"not with text describing the call.",
|
|
537
|
+
}
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
return messages
|
|
541
|
+
|
|
542
|
+
def execute_tool_call(
|
|
543
|
+
self, tool_calls: List, tool_functions: Dict[str, callable]
|
|
544
|
+
) -> List[Any]:
|
|
545
|
+
"""
|
|
546
|
+
Execute tool calls and collect results.
|
|
547
|
+
|
|
548
|
+
Args:
|
|
549
|
+
tool_calls: List of tool call objects from response
|
|
550
|
+
tool_functions: Dict mapping tool names to functions
|
|
551
|
+
|
|
552
|
+
Returns:
|
|
553
|
+
List of tool results
|
|
554
|
+
"""
|
|
555
|
+
results = []
|
|
556
|
+
|
|
557
|
+
for tool_call in tool_calls:
|
|
558
|
+
tool_name = tool_call.function.name
|
|
559
|
+
tool_args = json.loads(tool_call.function.arguments)
|
|
560
|
+
|
|
561
|
+
logger.info(f"Executing tool: {tool_name} with args: {tool_args}")
|
|
562
|
+
|
|
563
|
+
if tool_name not in tool_functions:
|
|
564
|
+
results.append(
|
|
565
|
+
{
|
|
566
|
+
"tool_name": tool_name,
|
|
567
|
+
"status": "error",
|
|
568
|
+
"error": f"Tool '{tool_name}' not found",
|
|
569
|
+
}
|
|
570
|
+
)
|
|
571
|
+
continue
|
|
572
|
+
|
|
573
|
+
try:
|
|
574
|
+
result = tool_functions[tool_name](**tool_args)
|
|
575
|
+
results.append(
|
|
576
|
+
{"tool_name": tool_name, "status": "success", "result": result}
|
|
577
|
+
)
|
|
578
|
+
except Exception as e:
|
|
579
|
+
results.append(
|
|
580
|
+
{"tool_name": tool_name, "status": "error", "error": str(e)}
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
return results
|
|
584
|
+
|
|
585
|
+
def get_metrics(self) -> ToolCallMetrics:
|
|
586
|
+
"""Get current metrics"""
|
|
587
|
+
return self.metrics
|
|
588
|
+
|
|
589
|
+
def reset_metrics(self):
|
|
590
|
+
"""Reset metrics counters"""
|
|
591
|
+
self.metrics = ToolCallMetrics()
|
|
592
|
+
logger.info("Metrics reset")
|
|
593
|
+
|
|
594
|
+
def get_status(self) -> Dict:
|
|
595
|
+
"""Get client status"""
|
|
596
|
+
return {
|
|
597
|
+
"profile": self.profile_name,
|
|
598
|
+
"model": self.config["model"],
|
|
599
|
+
"base_url": self.config["base_url"],
|
|
600
|
+
"metrics": self.metrics.to_dict(),
|
|
601
|
+
"config": {
|
|
602
|
+
k: v for k, v in self.config.items() if k not in ["base_url", "api_key"]
|
|
603
|
+
},
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
class ToolCallAgent:
|
|
608
|
+
"""
|
|
609
|
+
Higher-level agent for tool calling workflows.
|
|
610
|
+
|
|
611
|
+
Combines tool calling with execution and result handling.
|
|
612
|
+
Works with any OpenAI-compatible model.
|
|
613
|
+
|
|
614
|
+
Args:
|
|
615
|
+
tool_definitions: List of OpenAI-format tool definitions (for the API)
|
|
616
|
+
tool_implementations: Dict mapping tool names to callable functions (for execution)
|
|
617
|
+
client_config: Configuration for tool call client
|
|
618
|
+
"""
|
|
619
|
+
|
|
620
|
+
def __init__(
|
|
621
|
+
self,
|
|
622
|
+
tool_definitions: List[Dict[str, Any]],
|
|
623
|
+
tool_implementations: Dict[str, callable],
|
|
624
|
+
client_config: Optional[Dict] = None,
|
|
625
|
+
):
|
|
626
|
+
self.tool_definitions = tool_definitions
|
|
627
|
+
self.tool_implementations = tool_implementations
|
|
628
|
+
self.client = ToolCallClient(client_config)
|
|
629
|
+
|
|
630
|
+
logger.info(
|
|
631
|
+
f"ToolCallAgent initialized with {len(tool_definitions)} tools: "
|
|
632
|
+
f"{list(tool_implementations.keys())}"
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
def run(
|
|
636
|
+
self,
|
|
637
|
+
user_query: str,
|
|
638
|
+
additional_messages: Optional[List[Dict]] = None,
|
|
639
|
+
expected_tool: Optional[str] = None,
|
|
640
|
+
) -> Tuple[openai.types.chat.ChatCompletion, List[Any]]:
|
|
641
|
+
"""
|
|
642
|
+
Run a complete tool calling workflow.
|
|
643
|
+
|
|
644
|
+
Args:
|
|
645
|
+
user_query: User's input query
|
|
646
|
+
additional_messages: Optional additional conversation history
|
|
647
|
+
expected_tool: If set, use per-tool tool_choice
|
|
648
|
+
|
|
649
|
+
Returns:
|
|
650
|
+
Tuple of (ChatCompletion response, List of tool results)
|
|
651
|
+
"""
|
|
652
|
+
# Build messages
|
|
653
|
+
messages = []
|
|
654
|
+
|
|
655
|
+
if additional_messages:
|
|
656
|
+
messages.extend(additional_messages)
|
|
657
|
+
|
|
658
|
+
messages.append({"role": "user", "content": user_query})
|
|
659
|
+
|
|
660
|
+
# Make tool call request (pass definitions to API)
|
|
661
|
+
response = self.client.chat_with_tools(
|
|
662
|
+
messages=messages,
|
|
663
|
+
tools=self.tool_definitions,
|
|
664
|
+
expected_tool=expected_tool,
|
|
665
|
+
)
|
|
666
|
+
|
|
667
|
+
# Execute tool calls (pass implementations for execution)
|
|
668
|
+
tool_calls = response.choices[0].message.tool_calls
|
|
669
|
+
|
|
670
|
+
if tool_calls:
|
|
671
|
+
results = self.client.execute_tool_call(
|
|
672
|
+
tool_calls, self.tool_implementations
|
|
673
|
+
)
|
|
674
|
+
else:
|
|
675
|
+
results = []
|
|
676
|
+
|
|
677
|
+
return response, results
|
|
678
|
+
|
|
679
|
+
def get_status(self) -> Dict:
|
|
680
|
+
"""Get agent status"""
|
|
681
|
+
return {
|
|
682
|
+
"tools": list(self.tool_implementations.keys()),
|
|
683
|
+
"client_status": self.client.get_status(),
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
# ── Backward Compatibility Aliases ──────────────────────────────────────────
|
|
688
|
+
|
|
689
|
+
# Legacy names still work for existing code
|
|
690
|
+
Qwen35ToolCallClient = ToolCallClient
|
|
691
|
+
Qwen35ToolCallAgent = ToolCallAgent
|
|
692
|
+
Qwen35ToolCallError = ToolCallError
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
# ── Convenience Function ────────────────────────────────────────────────────
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
def chat_with_tools(
|
|
699
|
+
user_query: str,
|
|
700
|
+
tool_definitions: List[Dict],
|
|
701
|
+
tool_implementations: Dict[str, callable],
|
|
702
|
+
config: Optional[Dict] = None,
|
|
703
|
+
) -> Tuple[openai.types.chat.ChatCompletion, List[Any]]:
|
|
704
|
+
"""
|
|
705
|
+
Simple function for quick tool calling.
|
|
706
|
+
|
|
707
|
+
Args:
|
|
708
|
+
user_query: User's input
|
|
709
|
+
tool_definitions: OpenAI-format tool definitions
|
|
710
|
+
tool_implementations: Dict mapping tool names to callables
|
|
711
|
+
config: Optional configuration (can include "model_profile" key)
|
|
712
|
+
|
|
713
|
+
Returns:
|
|
714
|
+
Tuple of (response, results)
|
|
715
|
+
"""
|
|
716
|
+
agent = ToolCallAgent(tool_definitions, tool_implementations, config)
|
|
717
|
+
return agent.run(user_query)
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
# Legacy alias
|
|
721
|
+
qwen35_chat_with_tools = chat_with_tools
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
# ── Example Usage ───────────────────────────────────────────────────────────
|
|
725
|
+
|
|
726
|
+
if __name__ == "__main__":
|
|
727
|
+
# Example tool implementations (actual callables)
|
|
728
|
+
def read_file(path: str) -> str:
|
|
729
|
+
"""Read file contents"""
|
|
730
|
+
with open(path, "r") as f:
|
|
731
|
+
return f.read()
|
|
732
|
+
|
|
733
|
+
def get_weather(city: str) -> str:
|
|
734
|
+
"""Get weather for city"""
|
|
735
|
+
return f"Weather in {city}: Sunny, 25C"
|
|
736
|
+
|
|
737
|
+
# Tool implementations: name -> callable
|
|
738
|
+
implementations = {
|
|
739
|
+
"read_file": read_file,
|
|
740
|
+
"get_weather": get_weather,
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
# Tool definitions: OpenAI-format schemas (sent to the API)
|
|
744
|
+
definitions = [
|
|
745
|
+
{
|
|
746
|
+
"type": "function",
|
|
747
|
+
"function": {
|
|
748
|
+
"name": "read_file",
|
|
749
|
+
"description": "Read file contents",
|
|
750
|
+
"parameters": {
|
|
751
|
+
"type": "object",
|
|
752
|
+
"properties": {"path": {"type": "string"}},
|
|
753
|
+
"required": ["path"],
|
|
754
|
+
},
|
|
755
|
+
},
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
"type": "function",
|
|
759
|
+
"function": {
|
|
760
|
+
"name": "get_weather",
|
|
761
|
+
"description": "Get weather information",
|
|
762
|
+
"parameters": {
|
|
763
|
+
"type": "object",
|
|
764
|
+
"properties": {"city": {"type": "string"}},
|
|
765
|
+
"required": ["city"],
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
},
|
|
769
|
+
]
|
|
770
|
+
|
|
771
|
+
# Initialize agent with both definitions and implementations
|
|
772
|
+
agent = ToolCallAgent(definitions, implementations)
|
|
773
|
+
|
|
774
|
+
# Run example
|
|
775
|
+
try:
|
|
776
|
+
response, results = agent.run("Read /etc/hosts and get weather in Sydney")
|
|
777
|
+
|
|
778
|
+
print("\n=== Tool Call Results ===")
|
|
779
|
+
for result in results:
|
|
780
|
+
print(f"Tool: {result['tool_name']}")
|
|
781
|
+
print(f"Status: {result['status']}")
|
|
782
|
+
if result["status"] == "success":
|
|
783
|
+
print(f"Result: {str(result['result'])[:200]}...")
|
|
784
|
+
else:
|
|
785
|
+
print(f"Error: {result['error']}")
|
|
786
|
+
|
|
787
|
+
print("\n=== Metrics ===")
|
|
788
|
+
metrics = agent.client.get_metrics()
|
|
789
|
+
print(f"Total attempts: {metrics.total_attempts}")
|
|
790
|
+
print(f"Successful calls: {metrics.successful_calls}")
|
|
791
|
+
print(f"Escalations: {metrics.tool_choice_escalations}")
|
|
792
|
+
print(f"Parallel requested: {metrics.parallel_calls_requested}")
|
|
793
|
+
print(f"Parallel received: {metrics.parallel_calls_received}")
|
|
794
|
+
print(
|
|
795
|
+
f"Success rate: {metrics.successful_calls / metrics.total_attempts * 100:.1f}%"
|
|
796
|
+
)
|
|
797
|
+
|
|
798
|
+
except ToolCallError as e:
|
|
799
|
+
print(f"Tool call failed: {e}")
|