agent-toolkit-cli 1.0.3__py3-none-any.whl
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.
- agent_toolkit/__init__.py +4 -0
- agent_toolkit/__main__.py +3 -0
- agent_toolkit/_paths.py +64 -0
- agent_toolkit/cli/__init__.py +0 -0
- agent_toolkit/cli/doctor.py +453 -0
- agent_toolkit/cli/install.py +445 -0
- agent_toolkit/cli/main.py +66 -0
- agent_toolkit/cli/mcp.py +418 -0
- agent_toolkit/cli/plugin.py +213 -0
- agent_toolkit/cli/skills.py +421 -0
- agent_toolkit/loop/__init__.py +0 -0
- agent_toolkit/loop/gh_gate.py +626 -0
- agent_toolkit/loop/runner.py +1548 -0
- agent_toolkit/runner/__init__.py +0 -0
- agent_toolkit/runner/dispatcher.py +164 -0
- agent_toolkit/runner/policy.py +345 -0
- agent_toolkit/runner/providers/__init__.py +21 -0
- agent_toolkit/runner/providers/anthropic_provider.py +109 -0
- agent_toolkit/runner/providers/base.py +53 -0
- agent_toolkit/runner/providers/ollama_provider.py +108 -0
- agent_toolkit/runner/providers/openai_provider.py +105 -0
- agent_toolkit/runner/providers/opencode_provider.py +127 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/architect/AGENT.md +30 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/assistant/AGENT.md +59 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/build-error-resolver/AGENT.md +30 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/client-workflow-bootstrap/AGENT.md +60 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/code-reviewer/AGENT.md +49 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/database-reviewer/AGENT.md +44 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/docs-lookup/AGENT.md +28 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/e2e-runner/AGENT.md +38 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/performance-optimizer/AGENT.md +52 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/planner/AGENT.md +41 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/refactor-cleaner/AGENT.md +43 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/reference-lookup/AGENT.md +33 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/security-reviewer/AGENT.md +47 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/tdd-guide/AGENT.md +58 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/tech-assistant/AGENT.md +11 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/agents/typescript-reviewer/AGENT.md +61 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/catalogs/agent-catalog.yaml +202 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/catalogs/loop-catalog.yaml +165 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/catalogs/skill-catalog.yaml +699 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/catalogs/skills-layout.json +337 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/loops/changelog-drafter/loop.yaml +40 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/loops/ci-sweeper/loop.yaml +53 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/loops/daily-triage/loop.yaml +47 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/loops/dep-sweeper/loop.yaml +46 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/loops/issue-triage/loop.yaml +40 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/loops/oss-daily-briefing/loop.yaml +65 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/loops/oss-pr-monitor/loop.yaml +85 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/loops/oss-triage/loop.yaml +67 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/loops/post-merge-cleanup/loop.yaml +44 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/loops/pr-babysitter/loop.yaml +46 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/clickup/README.md +45 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/clickup/config.template.json +7 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/clickup/wrapper.sh +23 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/figma/README.md +56 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/figma/config.template.json +9 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/github/README.md +11 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/github/config.template.json +7 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/github/wrapper.sh +23 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/linear/README.md +49 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/linear/config.template.json +10 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/notion/README.md +7 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/notion/config.template.json +7 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/notion/wrapper.sh +23 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/slack/README.md +8 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/slack/config.template.json +8 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/mcp/templates/slack/wrapper.sh +23 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/CLAUDE.md +66 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/assistant.md +59 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/build-error-resolver.md +30 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/client-workflow-bootstrap.md +60 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/contribution-planner.md +39 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/database-reviewer.md +44 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/docs-lookup.md +28 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/e2e-runner.md +38 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/performance-optimizer.md +52 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/planner.md +41 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/refactor-cleaner.md +43 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/reference-lookup.md +33 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/security-reviewer.md +47 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/tdd-guide.md +58 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/agents/typescript-reviewer.md +61 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/claude-code/settings.json +87 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/copilot/copilot-instructions.md +15 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/README.md +33 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/architect.mdc +23 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/assistant.mdc +25 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/build-error-resolver.mdc +20 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/code-reviewer.mdc +21 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/database-reviewer.mdc +29 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/docs-lookup.mdc +15 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/e2e-runner.mdc +30 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/performance-optimizer.mdc +27 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/planner.mdc +28 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/refactor-cleaner.mdc +26 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/reference-lookup.mdc +20 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/security-reviewer.mdc +20 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/tdd-guide.mdc +26 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/cursor/rules/typescript-reviewer.mdc +28 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/architect.md +30 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/assistant.md +52 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/build-error-resolver.md +31 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/client-workflow-bootstrap.md +66 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/code-reviewer.md +47 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/contribution-planner.md +44 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/database-reviewer.md +43 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/docs-lookup.md +28 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/e2e-runner.md +33 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/performance-optimizer.md +34 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/planner.md +27 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/refactor-cleaner.md +27 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/reference-lookup.md +27 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/security-reviewer.md +27 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/tdd-guide.md +27 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/agents/typescript-reviewer.md +30 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/opencode/opencode.json +37 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/pi/skills/architect/skill.md +5 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/pi/skills/assistant/skill.md +3 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/pi/skills/code-reviewer/skill.md +5 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/pi/skills/planner/skill.md +3 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/pi/skills/security-reviewer/skill.md +3 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/memories/global_rules.md +74 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/rules/architect.mdc +23 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/rules/build-error-resolver.mdc +20 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/rules/code-reviewer.mdc +21 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/rules/database-reviewer.mdc +29 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/rules/docs-lookup.mdc +15 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/rules/e2e-runner.mdc +30 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/rules/performance-optimizer.mdc +27 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/rules/planner.mdc +28 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/rules/refactor-cleaner.mdc +26 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/rules/security-reviewer.mdc +20 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/rules/tdd-guide.mdc +26 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/profiles/windsurf/rules/typescript-reviewer.mdc +28 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/core/assistant/SKILL.md +213 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/core/assistant/references/AGENTS_TEMPLATE.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/core/assistant/references/ORCHESTRATION.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/core/assistant/references/REPO_INSPECTION.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/core/dev-companion/SKILL.md +65 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/core/dev-companion/references/LOOP_GUARDRAILS.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/core/onboarding/SKILL.md +119 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/core/output-handshake/SKILL.md +24 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/core/pr-fallback/SKILL.md +33 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/core/pr-fallback/references/pr-body-default.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/core/workspace-knowledge-sync/SKILL.md +141 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/data/dbt-validation/SKILL.md +41 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/data/snowflake-validation/SKILL.md +38 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/adr/SKILL.md +46 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/adr/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/adr/references/example-001-graphql-adoption.md +66 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/agreement/SKILL.md +23 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/agreement/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/agreement/references/example-api-versioning.md +29 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/bug/SKILL.md +25 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/bug/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/bug/references/example-search-filter-bug.md +58 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/decision-log/SKILL.md +27 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/decision-log/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/decision-log/references/example-repository-pattern.md +27 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/development-workflow/SKILL.md +33 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/development-workflow/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/development-workflow/references/default-workflow-checklist.md +44 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/epic/SKILL.md +21 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/epic/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/epic/references/example-unified-dashboard.md +40 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/incident/SKILL.md +23 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/incident/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/incident/references/example-payment-outage.md +62 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/management-unit-assessment/SKILL.md +105 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/management-unit-assessment/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/management-unit-assessment/references/example-team-assessment.md +126 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/meeting-minutes/SKILL.md +24 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/meeting-minutes/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/meeting-minutes/references/example-weekly-sync.md +72 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/planning/SKILL.md +30 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/planning/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/planning/references/example-sprint-planning.md +62 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/prd/SKILL.md +44 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/prd/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/prd/references/example-unified-api.md +71 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/project-assessment/SKILL.md +90 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/project-assessment/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/project-assessment/references/example-platform-assessment.md +122 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/project-assessment-evidence/SKILL.md +88 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/project-assessment-evidence/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/project-assessment-evidence/references/example-evidence-map.md +46 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/spike/SKILL.md +23 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/spike/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/spike/references/example-graphql-federation.md +79 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/task/SKILL.md +20 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/task/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/task/references/example-nav-system.md +33 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/technical-unit-assessment/SKILL.md +58 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/technical-unit-assessment/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/technical-unit-assessment/references/example-frontend-assessment.md +112 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/technical-unit-assessment/references/indicator-groups.md +65 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/trd/SKILL.md +47 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/trd/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/trd/references/example-api-migration.md +122 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/user-story/SKILL.md +20 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/user-story/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/user-story/references/example-advanced-filtering.md +37 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/work-item/SKILL.md +30 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/work-item/references/default-template.md +0 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/work-item/references/example-routing-dashboard.md +23 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/workflow-client-bootstrap/SKILL.md +148 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/workflow-client-bootstrap/questions.yaml +165 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/delivery/workflow-generic-project/SKILL.md +78 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma/LICENSE.txt +2 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma/NOTICE.txt +25 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma/SKILL.md +95 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma/references/figma-mcp-config.md +82 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma/references/figma-tools-and-prompts.md +34 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-code-connect-components/LICENSE.txt +2 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-code-connect-components/NOTICE.txt +18 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-code-connect-components/SKILL.md +350 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-code-connect-components/references/mapping-checklist.md +7 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-code-connect-components/scripts/normalize_node_id.py +25 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-create-design-system-rules/LICENSE.txt +2 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-create-design-system-rules/NOTICE.txt +19 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-create-design-system-rules/SKILL.md +550 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-create-design-system-rules/references/rule-template.md +15 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-create-design-system-rules/scripts/check_agents_md.sh +9 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-create-new-file/LICENSE.txt +2 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-create-new-file/NOTICE.txt +17 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-create-new-file/SKILL.md +69 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-implement-design/LICENSE.txt +2 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-implement-design/NOTICE.txt +19 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/figma-implement-design/SKILL.md +264 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/SKILL.md +60 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/charts.csv +26 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/colors.csv +97 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/icons.csv +101 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/landing.csv +31 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/products.csv +97 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/react-performance.csv +45 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/react.csv +54 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/styles.csv +68 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/typography.csv +58 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/data/web-interface.csv +31 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/references/pre-delivery-checklist.md +39 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/references/search-reference.md +44 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/references/ui-rules.md +35 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/scripts/core.py +253 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/scripts/design_system.py +1067 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/design/ui-ux-pro-max/scripts/search.py +114 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/gh-address-comments/LICENSE.txt +202 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/gh-address-comments/NOTICE.txt +20 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/gh-address-comments/SKILL.md +69 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/gh-address-comments/scripts/fetch_comments.py +237 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/gh-contribution-planner/SKILL.md +157 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/gh-contribution-planner/scripts/inspect_contributions.py +800 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/gh-fix-ci/LICENSE.txt +201 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/gh-fix-ci/NOTICE.txt +20 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/gh-fix-ci/SKILL.md +71 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/gh-fix-ci/scripts/inspect_pr_checks.py +509 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/github-cli-workflow/SKILL.md +49 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/gitlab-cli-workflow/SKILL.md +44 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/workflow-client-bootstrap/SKILL.md +14 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/forge/workflow-generic-project/SKILL.md +14 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/integrations/clickup-cli/SKILL.md +75 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/integrations/clickup-cli/references/docs-management.md +57 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/integrations/clickup-cli/references/task-management.md +131 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/integrations/clickup-cli/references/time-tracking.md +38 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/integrations/linear/LICENSE.txt +202 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/integrations/linear/NOTICE.txt +23 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/integrations/linear/SKILL.md +131 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/integrations/slack-assistant/SKILL.md +134 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/integrations/slack-cli/SKILL.md +105 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/integrations/slack-cli/references/INSTALLATION.md +42 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/loops/loop-runner/SKILL.md +90 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/ops/docs-generator/SKILL.md +81 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/ops/llm-cost-advisor/SKILL.md +83 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/ops/triage/SKILL.md +117 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/jupyter-notebook/LICENSE.txt +201 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/jupyter-notebook/NOTICE.txt +27 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/jupyter-notebook/SKILL.md +142 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/jupyter-notebook/assets/experiment-template.ipynb +110 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/jupyter-notebook/assets/tutorial-template.ipynb +107 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/jupyter-notebook/references/experiment-patterns.md +10 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/jupyter-notebook/references/notebook-structure.md +17 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/jupyter-notebook/references/quality-checklist.md +11 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/jupyter-notebook/references/tutorial-patterns.md +9 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/jupyter-notebook/scripts/new_notebook.py +130 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/playwright-cli/LICENSE.txt +201 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/playwright-cli/NOTICE.txt +29 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/playwright-cli/SKILL.md +151 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/playwright-cli/references/cli.md +116 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/playwright-cli/references/workflows.md +98 -0
- agent_toolkit_cli-1.0.3.data/data/agent_toolkit/data/skills/tooling/playwright-cli/scripts/executable_playwright_cli.sh +25 -0
- agent_toolkit_cli-1.0.3.dist-info/METADATA +411 -0
- agent_toolkit_cli-1.0.3.dist-info/RECORD +310 -0
- agent_toolkit_cli-1.0.3.dist-info/WHEEL +4 -0
- agent_toolkit_cli-1.0.3.dist-info/entry_points.txt +2 -0
- agent_toolkit_cli-1.0.3.dist-info/licenses/LICENSE +21 -0
|
File without changes
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""LLM dispatcher for the dev-companion runner.
|
|
2
|
+
|
|
3
|
+
Selects the best available LLM provider, constrained by an :class:`LLMPolicy`
|
|
4
|
+
that captures client-driven privacy/billing requirements (allowlist, denylist,
|
|
5
|
+
pinned provider/model, fail-closed ``strict`` mode).
|
|
6
|
+
|
|
7
|
+
When no policy is supplied, behaviour is backwards-compatible: providers are
|
|
8
|
+
ordered by their hard-coded ``get_priority()`` and the first available one is
|
|
9
|
+
selected.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
import sys
|
|
20
|
+
from pathlib import Path as _Path
|
|
21
|
+
|
|
22
|
+
sys.path.insert(0, str(_Path(__file__).parent))
|
|
23
|
+
|
|
24
|
+
from policy import LLMPolicy
|
|
25
|
+
from providers import (
|
|
26
|
+
AnthropicProvider,
|
|
27
|
+
LLMProvider,
|
|
28
|
+
LLMResponse,
|
|
29
|
+
OllamaProvider,
|
|
30
|
+
OpenAIProvider,
|
|
31
|
+
OpenCodeProvider,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class LLMDispatcher:
|
|
38
|
+
"""Pick a provider that satisfies the active :class:`LLMPolicy`.
|
|
39
|
+
|
|
40
|
+
The dispatcher never silently falls back to a denied provider: if the
|
|
41
|
+
policy filters every provider out and ``strict`` is set, the caller gets
|
|
42
|
+
an explicit ``policy_no_provider_available`` error. Non-strict mode keeps
|
|
43
|
+
the previous behaviour (returns ``None``; caller can use a skeleton plan).
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
POLICY_VIOLATION = "policy_no_provider_available"
|
|
47
|
+
|
|
48
|
+
def __init__(self, policy: LLMPolicy | None = None) -> None:
|
|
49
|
+
self._policy: LLMPolicy = policy or LLMPolicy.empty()
|
|
50
|
+
self._providers: list[LLMProvider] = []
|
|
51
|
+
self._candidates: list[LLMProvider] = []
|
|
52
|
+
self._selected: Optional[LLMProvider] = None
|
|
53
|
+
self._initialize_providers()
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def policy(self) -> LLMPolicy:
|
|
57
|
+
return self._policy
|
|
58
|
+
|
|
59
|
+
def _initialize_providers(self) -> None:
|
|
60
|
+
all_providers: list[LLMProvider] = [
|
|
61
|
+
OpenCodeProvider(),
|
|
62
|
+
OllamaProvider(),
|
|
63
|
+
AnthropicProvider(),
|
|
64
|
+
OpenAIProvider(),
|
|
65
|
+
]
|
|
66
|
+
# Stable hard-coded ordering for the unfiltered case.
|
|
67
|
+
all_providers.sort(key=lambda p: p.get_priority())
|
|
68
|
+
self._providers = all_providers
|
|
69
|
+
self._candidates = self._policy.filter(all_providers)
|
|
70
|
+
|
|
71
|
+
def list_candidates(self) -> list[dict]:
|
|
72
|
+
"""Detailed status for every candidate after policy filtering, without
|
|
73
|
+
executing the model. Used by the ``llm-status`` diagnostic."""
|
|
74
|
+
return [
|
|
75
|
+
{
|
|
76
|
+
"name": p.name,
|
|
77
|
+
"model": p.get_model_name(),
|
|
78
|
+
"available": p.is_available(),
|
|
79
|
+
"is_local": getattr(p, "is_local", False),
|
|
80
|
+
"is_free": getattr(p, "is_free", False),
|
|
81
|
+
"priority": p.get_priority(),
|
|
82
|
+
}
|
|
83
|
+
for p in self._candidates
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
def list_all_providers(self) -> list[dict]:
|
|
87
|
+
"""Status for every known provider, regardless of policy. Useful for
|
|
88
|
+
operators who need to see what was filtered out and why."""
|
|
89
|
+
candidate_names = {p.name for p in self._candidates}
|
|
90
|
+
return [
|
|
91
|
+
{
|
|
92
|
+
"name": p.name,
|
|
93
|
+
"model": p.get_model_name(),
|
|
94
|
+
"available": p.is_available(),
|
|
95
|
+
"is_local": getattr(p, "is_local", False),
|
|
96
|
+
"is_free": getattr(p, "is_free", False),
|
|
97
|
+
"priority": p.get_priority(),
|
|
98
|
+
"in_policy": p.name in candidate_names,
|
|
99
|
+
}
|
|
100
|
+
for p in self._providers
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
def get_available_provider(self) -> Optional[LLMProvider]:
|
|
104
|
+
if not self._candidates:
|
|
105
|
+
logger.warning(
|
|
106
|
+
"LLM policy filtered out every provider (allowlist=%s, deny=%s, pinned=%s)",
|
|
107
|
+
list(self._policy.allowlist) if self._policy.allowlist else None,
|
|
108
|
+
list(self._policy.denylist),
|
|
109
|
+
self._policy.pinned_provider,
|
|
110
|
+
)
|
|
111
|
+
return None
|
|
112
|
+
for provider in self._candidates:
|
|
113
|
+
if provider.is_available():
|
|
114
|
+
logger.info(
|
|
115
|
+
"Selected provider: %s (model: %s)",
|
|
116
|
+
provider.name,
|
|
117
|
+
provider.get_model_name(),
|
|
118
|
+
)
|
|
119
|
+
self._selected = provider
|
|
120
|
+
return provider
|
|
121
|
+
logger.warning(
|
|
122
|
+
"No allowed LLM provider is available (strict=%s)",
|
|
123
|
+
self._policy.strict,
|
|
124
|
+
)
|
|
125
|
+
return None
|
|
126
|
+
|
|
127
|
+
def get_selected(self) -> Optional[LLMProvider]:
|
|
128
|
+
return self._selected
|
|
129
|
+
|
|
130
|
+
def list_providers(self) -> list[dict]:
|
|
131
|
+
# Backwards-compatible alias used by ``dots_ai_devcompanion_runner``.
|
|
132
|
+
return self.list_candidates() or self.list_all_providers()
|
|
133
|
+
|
|
134
|
+
def generate(
|
|
135
|
+
self,
|
|
136
|
+
prompt: str,
|
|
137
|
+
system_prompt: Optional[str] = None,
|
|
138
|
+
repo_path: Optional[Path] = None,
|
|
139
|
+
timeout_sec: int = 300,
|
|
140
|
+
) -> tuple[Optional[LLMResponse], Optional[str]]:
|
|
141
|
+
provider = self.get_selected()
|
|
142
|
+
if not provider:
|
|
143
|
+
if self._policy.strict:
|
|
144
|
+
return None, self.POLICY_VIOLATION
|
|
145
|
+
return None, "no_provider"
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
response = provider.generate(
|
|
149
|
+
prompt=prompt,
|
|
150
|
+
system_prompt=system_prompt,
|
|
151
|
+
repo_path=repo_path,
|
|
152
|
+
timeout_sec=timeout_sec,
|
|
153
|
+
)
|
|
154
|
+
return response, None
|
|
155
|
+
except Exception as exc: # pragma: no cover - safety net
|
|
156
|
+
logger.error("Provider %s failed: %s", provider.name, exc)
|
|
157
|
+
return None, str(exc)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def create_dispatcher(policy: LLMPolicy | None = None) -> LLMDispatcher:
|
|
161
|
+
debug = os.environ.get("DOTS_WORKSTATION_LLM_DEBUG", "").lower() in ("1", "true", "yes", "on")
|
|
162
|
+
if debug:
|
|
163
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
164
|
+
return LLMDispatcher(policy=policy)
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"""LLM execution policy for the agent-toolkit dev-companion runner.
|
|
2
|
+
|
|
3
|
+
Defines which LLM providers are allowed to run, in what order, and whether
|
|
4
|
+
the runner is required to fail closed when no allowed provider is available
|
|
5
|
+
(for client engagements that mandate a single billing/data identity).
|
|
6
|
+
|
|
7
|
+
Policy sources, in order of precedence — each layer can only RESTRICT the
|
|
8
|
+
prior one, never widen it:
|
|
9
|
+
|
|
10
|
+
1. Global environment variables (machine baseline / engagement env file).
|
|
11
|
+
2. Optional JSON config file at ``~/.config/agent-toolkit/devcompanion-llm.json``
|
|
12
|
+
(path overridable with ``DOTS_WORKSTATION_DEVCOMPANION_LLM_CONFIG``).
|
|
13
|
+
3. Per-job ``llm`` object inside the ``.job`` JSON.
|
|
14
|
+
|
|
15
|
+
A job may further restrict (subset of ``allowlist``, additional ``denylist``
|
|
16
|
+
entries, ``strict: true``) but cannot expand the global allowlist or relax
|
|
17
|
+
strict mode that was set globally.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import json
|
|
23
|
+
import logging
|
|
24
|
+
import os
|
|
25
|
+
from dataclasses import dataclass, field
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Any, Iterable, Mapping
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
KNOWN_PROVIDERS: tuple[str, ...] = ("opencode", "ollama", "anthropic", "openai")
|
|
32
|
+
DEFAULT_CONFIG_PATH = Path.home() / ".config" / "agent-toolkit" / "devcompanion-llm.json"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PolicyError(ValueError):
|
|
36
|
+
"""Raised when policy values are inconsistent (for example a job tries to widen
|
|
37
|
+
the globally allowed set, or a pinned provider is denied)."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _truthy(value: Any) -> bool:
|
|
41
|
+
if isinstance(value, bool):
|
|
42
|
+
return value
|
|
43
|
+
if value is None:
|
|
44
|
+
return False
|
|
45
|
+
return str(value).strip().lower() in ("1", "true", "yes", "on")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _split_list(value: Any) -> tuple[str, ...]:
|
|
49
|
+
if value is None:
|
|
50
|
+
return ()
|
|
51
|
+
if isinstance(value, str):
|
|
52
|
+
items: list[str] = [s.strip() for s in value.split(",")]
|
|
53
|
+
elif isinstance(value, Iterable) and not isinstance(value, (bytes, bytearray)):
|
|
54
|
+
items = [str(s).strip() for s in value]
|
|
55
|
+
else:
|
|
56
|
+
return ()
|
|
57
|
+
return tuple(item.lower() for item in items if item)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _normalize_name(name: Any) -> str | None:
|
|
61
|
+
if name is None:
|
|
62
|
+
return None
|
|
63
|
+
text = str(name).strip().lower()
|
|
64
|
+
return text or None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(frozen=True)
|
|
68
|
+
class LLMPolicy:
|
|
69
|
+
"""Effective execution policy for the LLM dispatcher.
|
|
70
|
+
|
|
71
|
+
``allowlist`` preserves order: when set, the dispatcher keeps providers in
|
|
72
|
+
that order rather than the implementation's hard-coded priority. This lets
|
|
73
|
+
operators express "prefer Anthropic first, then OpenAI" explicitly.
|
|
74
|
+
|
|
75
|
+
``sources`` is for audit only; it lists which layers contributed to the
|
|
76
|
+
effective policy (``env``, ``file``, ``job``).
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
allowlist: tuple[str, ...] | None = None
|
|
80
|
+
denylist: tuple[str, ...] = ()
|
|
81
|
+
pinned_provider: str | None = None
|
|
82
|
+
pinned_model: str | None = None
|
|
83
|
+
strict: bool = False
|
|
84
|
+
sources: tuple[str, ...] = ()
|
|
85
|
+
warnings: tuple[str, ...] = field(default_factory=tuple)
|
|
86
|
+
|
|
87
|
+
# ── loaders ────────────────────────────────────────────────────────────
|
|
88
|
+
@classmethod
|
|
89
|
+
def empty(cls) -> "LLMPolicy":
|
|
90
|
+
return cls()
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def from_env(cls, env: Mapping[str, str] | None = None) -> "LLMPolicy":
|
|
94
|
+
env = os.environ if env is None else env
|
|
95
|
+
allow = _split_list(env.get("DOTS_WORKSTATION_DEVCOMPANION_LLM_ALLOWLIST"))
|
|
96
|
+
deny = _split_list(env.get("DOTS_WORKSTATION_DEVCOMPANION_LLM_DENYLIST"))
|
|
97
|
+
pinned = _normalize_name(env.get("DOTS_WORKSTATION_DEVCOMPANION_LLM_PINNED_PROVIDER"))
|
|
98
|
+
pinned_model_raw = env.get("DOTS_WORKSTATION_DEVCOMPANION_LLM_PINNED_MODEL")
|
|
99
|
+
pinned_model = (
|
|
100
|
+
pinned_model_raw.strip() if isinstance(pinned_model_raw, str) and pinned_model_raw.strip() else None
|
|
101
|
+
)
|
|
102
|
+
strict = _truthy(env.get("DOTS_WORKSTATION_DEVCOMPANION_LLM_STRICT"))
|
|
103
|
+
|
|
104
|
+
warnings = list(_unknown_warnings("env", allow, deny, pinned))
|
|
105
|
+
sources = ("env",) if (allow or deny or pinned or pinned_model or strict) else ()
|
|
106
|
+
return cls(
|
|
107
|
+
allowlist=allow or None,
|
|
108
|
+
denylist=deny,
|
|
109
|
+
pinned_provider=pinned,
|
|
110
|
+
pinned_model=pinned_model,
|
|
111
|
+
strict=strict,
|
|
112
|
+
sources=sources,
|
|
113
|
+
warnings=tuple(warnings),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
@classmethod
|
|
117
|
+
def from_file(cls, path: Path | str | None = None) -> "LLMPolicy":
|
|
118
|
+
env_override = os.environ.get("DOTS_WORKSTATION_DEVCOMPANION_LLM_CONFIG")
|
|
119
|
+
if path is None:
|
|
120
|
+
path = Path(env_override) if env_override else DEFAULT_CONFIG_PATH
|
|
121
|
+
path = Path(path).expanduser()
|
|
122
|
+
if not path.is_file():
|
|
123
|
+
return cls.empty()
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
127
|
+
except Exception as exc:
|
|
128
|
+
return cls(
|
|
129
|
+
warnings=(f"failed to parse policy file {path}: {exc}",),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if not isinstance(data, Mapping):
|
|
133
|
+
return cls(
|
|
134
|
+
warnings=(f"policy file {path} must contain a JSON object",),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
allow = _split_list(data.get("allowlist"))
|
|
138
|
+
deny = _split_list(data.get("denylist"))
|
|
139
|
+
pinned = _normalize_name(data.get("pinned_provider") or data.get("provider"))
|
|
140
|
+
pinned_model = data.get("pinned_model") or data.get("model")
|
|
141
|
+
if isinstance(pinned_model, str):
|
|
142
|
+
pinned_model = pinned_model.strip() or None
|
|
143
|
+
else:
|
|
144
|
+
pinned_model = None
|
|
145
|
+
strict = _truthy(data.get("strict"))
|
|
146
|
+
warnings = list(_unknown_warnings(f"file {path}", allow, deny, pinned))
|
|
147
|
+
sources = (f"file:{path}",) if (allow or deny or pinned or pinned_model or strict) else ()
|
|
148
|
+
return cls(
|
|
149
|
+
allowlist=allow or None,
|
|
150
|
+
denylist=deny,
|
|
151
|
+
pinned_provider=pinned,
|
|
152
|
+
pinned_model=pinned_model,
|
|
153
|
+
strict=strict,
|
|
154
|
+
sources=sources,
|
|
155
|
+
warnings=tuple(warnings),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
def from_job(cls, job_llm: Any) -> "LLMPolicy":
|
|
160
|
+
if job_llm is None or isinstance(job_llm, bool):
|
|
161
|
+
return cls.empty()
|
|
162
|
+
if not isinstance(job_llm, Mapping):
|
|
163
|
+
return cls.empty()
|
|
164
|
+
|
|
165
|
+
allow = _split_list(job_llm.get("allowlist"))
|
|
166
|
+
deny = _split_list(job_llm.get("denylist"))
|
|
167
|
+
pinned = _normalize_name(job_llm.get("provider") or job_llm.get("pinned_provider"))
|
|
168
|
+
pinned_model = job_llm.get("model") or job_llm.get("pinned_model")
|
|
169
|
+
if isinstance(pinned_model, str):
|
|
170
|
+
pinned_model = pinned_model.strip() or None
|
|
171
|
+
else:
|
|
172
|
+
pinned_model = None
|
|
173
|
+
strict = _truthy(job_llm.get("strict"))
|
|
174
|
+
warnings = list(_unknown_warnings("job", allow, deny, pinned))
|
|
175
|
+
sources = ("job",) if (allow or deny or pinned or pinned_model or strict) else ()
|
|
176
|
+
return cls(
|
|
177
|
+
allowlist=allow or None,
|
|
178
|
+
denylist=deny,
|
|
179
|
+
pinned_provider=pinned,
|
|
180
|
+
pinned_model=pinned_model,
|
|
181
|
+
strict=strict,
|
|
182
|
+
sources=sources,
|
|
183
|
+
warnings=tuple(warnings),
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
@classmethod
|
|
187
|
+
def load(
|
|
188
|
+
cls,
|
|
189
|
+
*,
|
|
190
|
+
env: Mapping[str, str] | None = None,
|
|
191
|
+
config_path: Path | str | None = None,
|
|
192
|
+
job_llm: Any | None = None,
|
|
193
|
+
) -> "LLMPolicy":
|
|
194
|
+
"""Compose ``env`` → ``file`` → ``job`` and validate that later layers only
|
|
195
|
+
restrict (never widen) earlier ones. Raises :class:`PolicyError` when a job
|
|
196
|
+
tries to escape the global allowlist."""
|
|
197
|
+
base = merge_policies(cls.from_env(env), cls.from_file(config_path))
|
|
198
|
+
if job_llm is None:
|
|
199
|
+
return base
|
|
200
|
+
job = cls.from_job(job_llm)
|
|
201
|
+
return merge_policies(base, job, _validate_restriction=True)
|
|
202
|
+
|
|
203
|
+
# ── filtering ──────────────────────────────────────────────────────────
|
|
204
|
+
def filter(self, providers: Iterable[Any]) -> list[Any]:
|
|
205
|
+
"""Return providers permitted by the policy, ordered by preference.
|
|
206
|
+
|
|
207
|
+
Mutates the ``model`` attribute on a pinned provider when ``pinned_model``
|
|
208
|
+
is set, so the caller can use the pinned model without changing the
|
|
209
|
+
provider classes.
|
|
210
|
+
"""
|
|
211
|
+
by_name: dict[str, Any] = {}
|
|
212
|
+
for p in providers:
|
|
213
|
+
name = _provider_name(p)
|
|
214
|
+
if name:
|
|
215
|
+
by_name[name] = p
|
|
216
|
+
|
|
217
|
+
if self.pinned_provider:
|
|
218
|
+
if self.pinned_provider in self.denylist:
|
|
219
|
+
return []
|
|
220
|
+
provider = by_name.get(self.pinned_provider)
|
|
221
|
+
if provider is None:
|
|
222
|
+
return []
|
|
223
|
+
if self.pinned_model and hasattr(provider, "model"):
|
|
224
|
+
try:
|
|
225
|
+
setattr(provider, "model", self.pinned_model)
|
|
226
|
+
except Exception:
|
|
227
|
+
logger.debug("Could not set pinned model on provider %s", self.pinned_provider)
|
|
228
|
+
return [provider]
|
|
229
|
+
|
|
230
|
+
denied = set(self.denylist)
|
|
231
|
+
if self.allowlist:
|
|
232
|
+
ordered: list[Any] = []
|
|
233
|
+
for name in self.allowlist:
|
|
234
|
+
if name in denied:
|
|
235
|
+
continue
|
|
236
|
+
provider = by_name.get(name)
|
|
237
|
+
if provider is not None:
|
|
238
|
+
ordered.append(provider)
|
|
239
|
+
return ordered
|
|
240
|
+
|
|
241
|
+
sorted_providers = sorted(by_name.values(), key=lambda p: getattr(p, "get_priority", lambda: 100)())
|
|
242
|
+
return [p for p in sorted_providers if _provider_name(p) not in denied]
|
|
243
|
+
|
|
244
|
+
# ── audit ──────────────────────────────────────────────────────────────
|
|
245
|
+
def to_audit(self) -> dict[str, Any]:
|
|
246
|
+
return {
|
|
247
|
+
"allowlist": list(self.allowlist) if self.allowlist else None,
|
|
248
|
+
"denylist": list(self.denylist),
|
|
249
|
+
"pinned_provider": self.pinned_provider,
|
|
250
|
+
"pinned_model": self.pinned_model,
|
|
251
|
+
"strict": self.strict,
|
|
252
|
+
"sources": list(self.sources),
|
|
253
|
+
"warnings": list(self.warnings),
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _provider_name(provider: Any) -> str:
|
|
258
|
+
return str(getattr(provider, "name", "")).strip().lower()
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def _unknown_warnings(
|
|
262
|
+
layer: str,
|
|
263
|
+
allow: tuple[str, ...],
|
|
264
|
+
deny: tuple[str, ...],
|
|
265
|
+
pinned: str | None,
|
|
266
|
+
) -> Iterable[str]:
|
|
267
|
+
for name in allow:
|
|
268
|
+
if name not in KNOWN_PROVIDERS:
|
|
269
|
+
yield f"{layer}: allowlist contains unknown provider '{name}'"
|
|
270
|
+
for name in deny:
|
|
271
|
+
if name not in KNOWN_PROVIDERS:
|
|
272
|
+
yield f"{layer}: denylist contains unknown provider '{name}'"
|
|
273
|
+
if pinned and pinned not in KNOWN_PROVIDERS:
|
|
274
|
+
yield f"{layer}: pinned provider '{pinned}' is unknown"
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def merge_policies(
|
|
278
|
+
base: LLMPolicy,
|
|
279
|
+
overlay: LLMPolicy,
|
|
280
|
+
*,
|
|
281
|
+
_validate_restriction: bool = False,
|
|
282
|
+
) -> LLMPolicy:
|
|
283
|
+
"""Merge ``overlay`` on top of ``base``.
|
|
284
|
+
|
|
285
|
+
When ``_validate_restriction`` is true (job over global), ``overlay`` may only
|
|
286
|
+
restrict ``base``. Attempting to widen raises :class:`PolicyError`.
|
|
287
|
+
"""
|
|
288
|
+
# Allowlist
|
|
289
|
+
if overlay.allowlist is None:
|
|
290
|
+
merged_allow = base.allowlist
|
|
291
|
+
elif base.allowlist is None:
|
|
292
|
+
merged_allow = overlay.allowlist
|
|
293
|
+
else:
|
|
294
|
+
if _validate_restriction:
|
|
295
|
+
extras = [name for name in overlay.allowlist if name not in base.allowlist]
|
|
296
|
+
if extras:
|
|
297
|
+
raise PolicyError(
|
|
298
|
+
"job allowlist tries to add provider(s) not allowed globally: "
|
|
299
|
+
+ ", ".join(extras)
|
|
300
|
+
)
|
|
301
|
+
# Keep overlay order, but only entries also in base.
|
|
302
|
+
merged_allow = tuple(name for name in overlay.allowlist if name in base.allowlist)
|
|
303
|
+
if not merged_allow:
|
|
304
|
+
raise PolicyError(
|
|
305
|
+
"job allowlist is empty after intersecting with global allowlist"
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
# Denylist (always additive)
|
|
309
|
+
merged_deny = tuple(sorted(set(base.denylist) | set(overlay.denylist)))
|
|
310
|
+
|
|
311
|
+
# Pinned provider/model
|
|
312
|
+
merged_pin_provider = overlay.pinned_provider or base.pinned_provider
|
|
313
|
+
merged_pin_model = overlay.pinned_model or base.pinned_model
|
|
314
|
+
|
|
315
|
+
if _validate_restriction and overlay.pinned_provider:
|
|
316
|
+
if base.allowlist and overlay.pinned_provider not in base.allowlist:
|
|
317
|
+
raise PolicyError(
|
|
318
|
+
f"job pins provider '{overlay.pinned_provider}' but it is not in the global allowlist"
|
|
319
|
+
)
|
|
320
|
+
if overlay.pinned_provider in merged_deny:
|
|
321
|
+
raise PolicyError(
|
|
322
|
+
f"job pins provider '{overlay.pinned_provider}' but it is denied by policy"
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
# Strict can only escalate (False -> True). A job cannot lower a globally-strict policy.
|
|
326
|
+
merged_strict = base.strict or overlay.strict
|
|
327
|
+
if _validate_restriction and base.strict and overlay.strict is False and "job" in overlay.sources:
|
|
328
|
+
# Job explicitly set strict=false but base is strict: warn, keep True.
|
|
329
|
+
warnings = list(base.warnings) + list(overlay.warnings) + [
|
|
330
|
+
"job tried to set strict=false while global policy is strict; keeping strict=true",
|
|
331
|
+
]
|
|
332
|
+
else:
|
|
333
|
+
warnings = list(base.warnings) + list(overlay.warnings)
|
|
334
|
+
|
|
335
|
+
sources = tuple(dict.fromkeys(list(base.sources) + list(overlay.sources)))
|
|
336
|
+
|
|
337
|
+
return LLMPolicy(
|
|
338
|
+
allowlist=merged_allow,
|
|
339
|
+
denylist=merged_deny,
|
|
340
|
+
pinned_provider=merged_pin_provider,
|
|
341
|
+
pinned_model=merged_pin_model,
|
|
342
|
+
strict=merged_strict,
|
|
343
|
+
sources=sources,
|
|
344
|
+
warnings=tuple(warnings),
|
|
345
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
agent-toolkit dev-companion LLM providers.
|
|
3
|
+
|
|
4
|
+
Provider-agnostic abstraction layer for LLM generation.
|
|
5
|
+
Supports local (free) and cloud (paid) providers with automatic fallback.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .base import LLMProvider, LLMResponse
|
|
9
|
+
from .opencode_provider import OpenCodeProvider
|
|
10
|
+
from .ollama_provider import OllamaProvider
|
|
11
|
+
from .anthropic_provider import AnthropicProvider
|
|
12
|
+
from .openai_provider import OpenAIProvider
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"LLMProvider",
|
|
16
|
+
"LLMResponse",
|
|
17
|
+
"OpenCodeProvider",
|
|
18
|
+
"OllamaProvider",
|
|
19
|
+
"AnthropicProvider",
|
|
20
|
+
"OpenAIProvider",
|
|
21
|
+
]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Anthropic provider for dev-companion.
|
|
3
|
+
|
|
4
|
+
Uses Anthropic API (Claude) as optional paid provider.
|
|
5
|
+
Requires: ANTHROPIC_API_KEY environment variable.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import time
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
from .base import LLMProvider, LLMResponse
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AnthropicProvider(LLMProvider):
|
|
18
|
+
name = "anthropic"
|
|
19
|
+
is_local = False
|
|
20
|
+
is_free = False
|
|
21
|
+
|
|
22
|
+
def __init__(self, model: str = "claude-3-5-haiku-20241022"):
|
|
23
|
+
self.model = model
|
|
24
|
+
|
|
25
|
+
def is_available(self) -> bool:
|
|
26
|
+
return bool(os.environ.get("ANTHROPIC_API_KEY"))
|
|
27
|
+
|
|
28
|
+
def supports_directory_context(self) -> bool:
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
def get_model_name(self) -> str:
|
|
32
|
+
return self.model
|
|
33
|
+
|
|
34
|
+
def get_priority(self) -> int:
|
|
35
|
+
return 10
|
|
36
|
+
|
|
37
|
+
def generate(
|
|
38
|
+
self,
|
|
39
|
+
prompt: str,
|
|
40
|
+
system_prompt: Optional[str] = None,
|
|
41
|
+
repo_path: Optional[Path] = None,
|
|
42
|
+
timeout_sec: int = 300,
|
|
43
|
+
) -> LLMResponse:
|
|
44
|
+
import urllib.request
|
|
45
|
+
import urllib.error
|
|
46
|
+
|
|
47
|
+
start_time = time.time()
|
|
48
|
+
|
|
49
|
+
headers = {
|
|
50
|
+
"Content-Type": "application/json",
|
|
51
|
+
"x-api-key": os.environ["ANTHROPIC_API_KEY"],
|
|
52
|
+
"anthropic-version": "2023-06-01",
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
messages = [{"role": "user", "content": prompt}]
|
|
56
|
+
if system_prompt:
|
|
57
|
+
messages[0] = {
|
|
58
|
+
"role": "user",
|
|
59
|
+
"content": f"{system_prompt}\n\n---\n\n{prompt}",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
data = {
|
|
63
|
+
"model": self.model,
|
|
64
|
+
"messages": messages,
|
|
65
|
+
"max_tokens": 4096,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
req = urllib.request.Request(
|
|
70
|
+
"https://api.anthropic.com/v1/messages",
|
|
71
|
+
data=json.dumps(data).encode(),
|
|
72
|
+
headers=headers,
|
|
73
|
+
method="POST",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
with urllib.request.urlopen(req, timeout=timeout_sec) as response:
|
|
77
|
+
result = json.loads(response.read().decode())
|
|
78
|
+
|
|
79
|
+
duration = time.time() - start_time
|
|
80
|
+
|
|
81
|
+
content = ""
|
|
82
|
+
for block in result.get("content", []):
|
|
83
|
+
if block.get("type") == "text":
|
|
84
|
+
content += block.get("text", "")
|
|
85
|
+
|
|
86
|
+
usage = result.get("usage", {})
|
|
87
|
+
|
|
88
|
+
return LLMResponse(
|
|
89
|
+
content=content,
|
|
90
|
+
model=self.model,
|
|
91
|
+
provider=self.name,
|
|
92
|
+
tokens_used=usage.get("output_tokens"),
|
|
93
|
+
duration_sec=duration,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
except urllib.error.HTTPError as e:
|
|
97
|
+
return LLMResponse(
|
|
98
|
+
content=f"HTTP Error: {e.code} - {e.reason}",
|
|
99
|
+
model=self.model,
|
|
100
|
+
provider=self.name,
|
|
101
|
+
duration_sec=time.time() - start_time,
|
|
102
|
+
)
|
|
103
|
+
except Exception as e:
|
|
104
|
+
return LLMResponse(
|
|
105
|
+
content=f"Error: {str(e)}",
|
|
106
|
+
model=self.model,
|
|
107
|
+
provider=self.name,
|
|
108
|
+
duration_sec=time.time() - start_time,
|
|
109
|
+
)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base interface for LLM providers.
|
|
3
|
+
|
|
4
|
+
All providers must implement this interface to be used by the dev-companion runner.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class LLMResponse:
|
|
15
|
+
content: str
|
|
16
|
+
model: str
|
|
17
|
+
provider: str
|
|
18
|
+
tokens_used: Optional[int] = None
|
|
19
|
+
duration_sec: Optional[float] = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LLMProvider(ABC):
|
|
23
|
+
name: str
|
|
24
|
+
is_local: bool = False
|
|
25
|
+
is_free: bool = False
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def is_available(self) -> bool:
|
|
29
|
+
"""Check if the provider is available (installed, configured, etc.)"""
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def generate(
|
|
34
|
+
self,
|
|
35
|
+
prompt: str,
|
|
36
|
+
system_prompt: Optional[str] = None,
|
|
37
|
+
repo_path: Optional[Path] = None,
|
|
38
|
+
timeout_sec: int = 300,
|
|
39
|
+
) -> LLMResponse:
|
|
40
|
+
"""Generate a response from the LLM"""
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
def get_priority(self) -> int:
|
|
44
|
+
"""Lower = higher priority. Local free providers should have priority over cloud paid ones."""
|
|
45
|
+
return 100
|
|
46
|
+
|
|
47
|
+
def supports_directory_context(self) -> bool:
|
|
48
|
+
"""Whether the provider can operate in a specific directory context"""
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
def get_model_name(self) -> str:
|
|
52
|
+
"""Return the model name used by this provider"""
|
|
53
|
+
return "unknown"
|