diff --git a/.github/workflows/l9-pr-pipeline.yml b/.github/workflows/l9-pr-pipeline.yml index c6ae847..59d3165 100644 --- a/.github/workflows/l9-pr-pipeline.yml +++ b/.github/workflows/l9-pr-pipeline.yml @@ -1,4 +1,6 @@ -# Quantum-L9 org starter — Full PR validation pipeline (lint, type-check, tests, security). Calls l9-ci-core kernel; do not add logic here. +# Quantum-L9 — PR validate with GitHub Packages auth. +# l9-ci-core pr-pipeline@v1 omits registry-url/NODE_AUTH_TOKEN; private +# @quantum-l9/* deps then 401. Mirror CI job auth (ci.yml). name: "L9 PR Pipeline" on: pull_request: @@ -6,11 +8,24 @@ on: push: branches: [main] +permissions: + contents: read + packages: read + jobs: l9_pr_pipeline: - uses: Quantum-L9/l9-ci-core/.github/workflows/pr-pipeline.yml@v1 - secrets: inherit - with: - python-version: "3.12" - run-security: true - working-directory: "." + runs-on: ubuntu-latest + timeout-minutes: 30 + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "20.19.0" + cache: npm + registry-url: https://npm.pkg.github.com + - run: npm ci --ignore-scripts + - run: npm run lint + - run: npm run verify:types + - run: npm test diff --git a/package-lock.json b/package-lock.json index f4fb6ac..c5e5c55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@quantum-l9/llm-router", - "version": "1.1.0", + "version": "1.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@quantum-l9/llm-router", - "version": "1.1.0", + "version": "1.1.1", "license": "PROPRIETARY", "dependencies": { "@quantum-l9/graphiti-memory-client": "^2.0.0", diff --git a/package.json b/package.json index 512c682..acf4b7c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@quantum-l9/llm-router", - "version": "1.1.0", + "version": "1.1.1", "type": "module", "description": "Reusable multi-provider LLM routing module with governed l9-graphiti-memory hydration, task-to-model routing, budgets, search, vision, and provider resilience.", "main": "dist/index.js", diff --git a/src/providers/openrouter.ts b/src/providers/openrouter.ts index ff86d47..e351cfd 100644 --- a/src/providers/openrouter.ts +++ b/src/providers/openrouter.ts @@ -18,7 +18,7 @@ import { GeneralModel as GeneralModelValue } from '../types.js'; const MODEL_IDS: Record = { [GeneralModelValue.GPT4O_MINI]: 'openai/gpt-4o-mini', [GeneralModelValue.GEMINI_FLASH]: 'google/gemini-2.5-flash', - [GeneralModelValue.CLAUDE_HAIKU]: 'anthropic/claude-haiku-4', + [GeneralModelValue.CLAUDE_HAIKU]: 'anthropic/claude-haiku-4.5', [GeneralModelValue.GPT4O]: 'openai/gpt-4o', [GeneralModelValue.CLAUDE_SONNET]: 'anthropic/claude-sonnet-4', [GeneralModelValue.GEMINI_PRO]: 'google/gemini-2.5-pro', diff --git a/src/types.ts b/src/types.ts index a9d7b2e..c4690d8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,7 +17,7 @@ export enum SonarModel { export enum GeneralModel { GPT4O_MINI = 'openai/gpt-4o-mini', GEMINI_FLASH = 'google/gemini-2.5-flash', - CLAUDE_HAIKU = 'anthropic/claude-haiku-4', + CLAUDE_HAIKU = 'anthropic/claude-haiku-4.5', GPT4O = 'openai/gpt-4o', CLAUDE_SONNET = 'anthropic/claude-sonnet-4', GEMINI_PRO = 'google/gemini-2.5-pro', diff --git a/tests/haiku-model-id.test.ts b/tests/haiku-model-id.test.ts new file mode 100644 index 0000000..6bf9a71 --- /dev/null +++ b/tests/haiku-model-id.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest'; +import { resolveRoute } from '../src/index.js'; +import { OpenRouterClient } from '../src/providers/openrouter.js'; +import type { ChatCompletionRequest, ChatCompletionResult, ChatTransport } from '../src/providers/openai-transport.js'; +import { GeneralModel, Provider, TaskComplexity, TaskType, type GeneralModelConfig } from '../src/types.js'; + +class CapturingTransport implements ChatTransport { + requests: ChatCompletionRequest[] = []; + async create(request: ChatCompletionRequest): Promise { + this.requests.push(request); + return { id: 'req', choices: [{ message: { content: 'ok' } }], usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 } }; + } +} + +describe('CLAUDE_HAIKU OpenRouter model id', () => { + it('uses anthropic/claude-haiku-4.5 in enum and CODE_GENERATION+LOW routing', () => { + expect(GeneralModel.CLAUDE_HAIKU).toBe('anthropic/claude-haiku-4.5'); + const route = resolveRoute({ type: TaskType.CODE_GENERATION, complexity: TaskComplexity.LOW }); + expect(route.model).toBe(GeneralModel.CLAUDE_HAIKU); + }); + + it('maps CLAUDE_HAIKU to the OpenRouter request model id', async () => { + const transport = new CapturingTransport(); + const client = new OpenRouterClient('key', 'app', 1000, transport); + const config: GeneralModelConfig = { + model: GeneralModel.CLAUDE_HAIKU, + provider: Provider.OPENROUTER, + temperature: 0.1, + maxTokens: 100, + estimatedCostPerCall: 0.01, + resolutionReason: 'test', + }; + await client.complete(config, 'system', 'user'); + expect(transport.requests[0]?.model).toBe('anthropic/claude-haiku-4.5'); + }); +});