-
Notifications
You must be signed in to change notification settings - Fork 1
feat: org catalog + live discovery + cost-performance choose #642
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2f1ecd1
ca2dd9f
1e82827
11224b2
cdfcacf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| name: OpenCode sidecar | ||
|
|
||
| # Deploy/CI sidecar only. Does not run on pull_request so app tests and the | ||
| # required Security workflow stay secret-free (see tests.yml / security.yml). | ||
| on: | ||
| workflow_dispatch: | ||
| workflow_call: | ||
| secrets: | ||
| NVIDIA_NIM_API_KEY: | ||
| required: false | ||
| NVIDIA_NIM_API_KEY_SUB: | ||
| required: false | ||
| OPENAI_API_KEY: | ||
| required: false | ||
| OPENROUTER_API_KEY: | ||
| required: false | ||
| BYTEZ_API_KEY: | ||
| required: false | ||
| push: | ||
| branches: [main] | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: opencode-sidecar-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| seed_and_serve: | ||
| name: Seed KV and serve loopback sidecar | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # actions/checkout@v7 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # actions/setup-python@v6 | ||
| with: | ||
| python-version: "3.12" | ||
|
|
||
| - name: Install package | ||
| run: | | ||
| python -m pip install --require-hashes -r requirements.lock | ||
| python -m pip install --no-deps -e . | ||
|
|
||
| - name: Seed org credentials into this-job report | ||
| env: | ||
| NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} | ||
| NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} | ||
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | ||
| OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} | ||
| BYTEZ_API_KEY: ${{ secrets.BYTEZ_API_KEY }} | ||
| run: | | ||
| python -m contextual_orchestrator seed-provider-catalog \ | ||
| --from-env --skip-missing \ | ||
| --agents examples/agents.production.json \ | ||
| --agents-db "$RUNNER_TEMP/agents.db" \ | ||
| --discover-models | tee "$RUNNER_TEMP/seed-report.json" | ||
|
|
||
| - name: Serve loopback OpenAI-compatible API and smoke curl | ||
| env: | ||
| NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} | ||
| NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} | ||
| OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} | ||
| OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} | ||
| BYTEZ_API_KEY: ${{ secrets.BYTEZ_API_KEY }} | ||
| run: | | ||
| set -euo pipefail | ||
| CONTEXTUAL_ORCHESTRATOR_TOKEN="$(python -c 'import secrets; print(secrets.token_urlsafe(32))')" | ||
| export CONTEXTUAL_ORCHESTRATOR_TOKEN | ||
| python -m contextual_orchestrator --serve \ | ||
| --seed-from-env \ | ||
| --agents examples/agents.production.json \ | ||
| --agents-db "$RUNNER_TEMP/agents.db" \ | ||
| --host 127.0.0.1 \ | ||
| --port 8000 \ | ||
| --auth-token "$CONTEXTUAL_ORCHESTRATOR_TOKEN" & | ||
| server_pid=$! | ||
| trap 'kill "$server_pid" 2>/dev/null || true' EXIT | ||
| for _ in 1 2 3 4 5 6 7 8 9 10; do | ||
| if curl -sf http://127.0.0.1:8000/healthz >/dev/null; then | ||
| break | ||
| fi | ||
| sleep 1 | ||
| done | ||
| registered="$(python -c 'import json,sys; print(len(json.load(open(sys.argv[1]))["registered_credentials"]))' "$RUNNER_TEMP/seed-report.json")" | ||
| if [ "$registered" = "0" ]; then | ||
| echo "no provider secrets in this job; skip live chat smoke (fail-closed, no GitHub Models fallback)" | ||
| exit 0 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Medium — a dead sidecar still goes green when no provider secrets are present. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Medium — a dead sidecar still goes green when no provider secrets are present. If |
||
| fi | ||
|
Comment on lines
+83
to
+93
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Medium — a dead sidecar still goes green when no provider secrets are present. If |
||
| curl -sS --fail http://127.0.0.1:8000/v1/models \ | ||
| -H "authorization: Bearer $CONTEXTUAL_ORCHESTRATOR_TOKEN" | ||
| curl -sS --fail http://127.0.0.1:8000/v1/chat/completions \ | ||
| -H "authorization: Bearer $CONTEXTUAL_ORCHESTRATOR_TOKEN" \ | ||
| -H "content-type: application/json" \ | ||
| -d '{"model":"contextual-orchestrator","messages":[{"role":"user","content":"Write one sentence."}]}' | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -56,23 +56,30 @@ push or open a PR. | |
| - The reference implementation is xtrmLLMBatchPython's pgcrypto-encrypted | ||
| Postgres credential registry (`get_credential(name)`); reuse that pattern (a | ||
| DB-backed KV is fine) unless a dedicated KV is adopted. | ||
| - **Known deviation to migrate:** this repo currently resolves provider API | ||
| keys from env — `ModelClient` reads `os.environ.get(agent.api_key_env)` in | ||
| `contextual_orchestrator/orchestrator.py` (and `CONTEXTUAL_ORCHESTRATOR_*` | ||
| tokens in `__main__.py`). Move these to KV-backed reads; keep env only as the | ||
| bootstrap path that seeds the KV. | ||
| - Runtime provider keys resolve through `get_credential` / the KV registry. | ||
| Env is bootstrap transport only (`register-credential --from-env` / | ||
| `seed-provider-catalog --from-env` / `--seed-from-env` on serve). The five | ||
| org Actions secrets are `NVIDIA_NIM_API_KEY`, `NVIDIA_NIM_API_KEY_SUB`, | ||
| `OPENAI_API_KEY`, `OPENROUTER_API_KEY`, and `BYTEZ_API_KEY`. A missing secret | ||
| skips that upstream; it must not crash the pool. | ||
| - ContextualWisdomLab **no longer uses GitHub Models**. Do not add | ||
| `COPILOT_GITHUB_TOKEN`, `models.github.ai`, `gpt-5.6-luna`, or `gpt-5.6-terra`. | ||
|
|
||
| ### This repo: the org LLM gateway | ||
|
|
||
| - `contextual-orchestrator` is the org's **LLM-communication hub** — the | ||
| OpenAI-compatible front door consumed by **gyeot** and **scopeweave**. | ||
| OpenAI-compatible front door consumed by **gyeot**, **scopeweave**, | ||
| **OpenCode**, and **Strix**. | ||
| - **Direction:** grow it toward a **LiteLLM-class multi-provider gateway**. The | ||
| org is open to a **Rust/Python hybrid** to cut overhead. | ||
| - Its `ModelClient` currently reads `os.environ.get(agent.api_key_env)` — this | ||
| is the KV-principle deviation above. Resolve the API key (including the org | ||
| `OPENAI_API_KEY`) from the **KV / credential registry**, not env. | ||
| - The **OpenCode review pipeline is separate** and stays on **GitHub Models** — | ||
| do not change it. | ||
| - `ModelClient` resolves the API key from the **KV / credential registry** via | ||
| `get_credential` (including the org `OPENAI_API_KEY` and NIM / OpenRouter / | ||
| Bytez keys). Env is never the request-time source. | ||
|
Comment on lines
+75
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift 런타임 환경 변수 조회를 제거하십시오.
현재 문서의 “Env is never the request-time source” 설명은 실제 동작과 다릅니다. allowlist를 KV/registry에서 조회하도록 변경하고 이 계약을 유지하십시오. As per coding guidelines, 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| - OpenCode/Strix should call this process as **one** OpenAI-compatible | ||
| provider: `baseURL http://127.0.0.1:8000/v1`, model `contextual-orchestrator` | ||
| (see `docs/opencode-sidecar.md`). The org-central review workflow lives in | ||
| `ContextualWisdomLab/.github` and should consume this sidecar — do not | ||
| reintroduce GitHub Models there from this repo. | ||
|
|
||
| ### This repo's role in the ecosystem | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| # Changelog | ||
|
|
||
| ## Unreleased | ||
|
|
||
| ### Added | ||
|
|
||
| - Production agent catalog (`examples/agents.production.json`) for NVIDIA NIM | ||
| (primary + secondary Nemotron Super 49B / 120B), OpenAI, OpenRouter, and | ||
| Bytez. Capability tags cover coding, review, and reasoning so Fugu route vs | ||
| Conductor/TRINITY conduct can pick workers. GitHub Models, Copilot tokens, | ||
| `gpt-5.6-luna`, and `gpt-5.6-terra` are rejected. | ||
| - `seed-provider-catalog` CLI and `--seed-from-env` serve flag register the five | ||
| org Actions secrets (`NVIDIA_NIM_API_KEY`, `NVIDIA_NIM_API_KEY_SUB`, | ||
| `OPENAI_API_KEY`, `OPENROUTER_API_KEY`, `BYTEZ_API_KEY`) into the KV. A | ||
| missing secret skips that upstream. **Live `GET /v1/models` is the primary | ||
| catalog** after each secret is registered (KV credential, never request-time | ||
| `os.getenv`). The static seed is fallback only when the list is missing, | ||
| 401/403/404/429/5xx, empty, or malformed. The gateway exposes | ||
| `GET /v1/models` (`contextual-orchestrator` plus surfaced worker ids). | ||
| (`docs/doctoring/provider-catalog.md`). | ||
| - OpenCode/Strix sidecar contract: loopback `http://127.0.0.1:8000/v1`, model | ||
| `contextual-orchestrator` (`.github/workflows/opencode-sidecar.yml`, | ||
| `docs/opencode-sidecar.md`). App tests and Security stay secret-free. | ||
|
|
||
| ### Changed | ||
|
|
||
| - Fast-path routing is a cost-performance choose (quality per unit operator | ||
| cost), not deterministic keyword scoring and not a walk down the seed JSON. | ||
| 429 / 5xx / timeout re-runs the same chooser on the remaining healthy pool. | ||
| Missing credentials drop that worker from the candidate set. An empty | ||
| healthy pool fail-closes (no GitHub Models). Deep `conduct` stays | ||
| Conductor-style and still requires a workflow hint. | ||
| - Unconfigured remote workers are skipped at select/re-selection time. When every | ||
| provider credential is missing, routing raises `NotConfigured` and does not | ||
| fall back to GitHub Models. | ||
| - Malformed upstream chat.completion bodies raise `ProviderResponseError` so | ||
| the gateway failovers or returns a JSON error instead of crashing. | ||
| - Live catalog discovery reuses `ModelClient.fetch_provider_json` (the existing | ||
| validated urllib seam) instead of `http.client` or a second `urlopen`. | ||
| `file://` and private/reserved list targets fail closed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
health check 실패 시 workflow를 실패 처리하십시오.
현재 loop는
/healthz성공 여부를 확인하지 않습니다. 서버가 시작하지 않았고 등록된 시크릿 수가0이면 Line 92가 성공으로 종료합니다. 이 경우 sidecar 시작 실패가 녹색 workflow 결과로 숨겨집니다.수정 예시
📝 Committable suggestion
🤖 Prompt for AI Agents