feat(#125): add Claude (Anthropic) LLM provider

Implement Anthropic Claude provider for Claude Opus, Sonnet, and Haiku models.

Implementation details:
- Created ClaudeProvider class implementing LlmProviderInterface
- Added @anthropic-ai/sdk npm package integration
- Implemented chat completion with streaming support
- Claude-specific message format (system prompt separate from messages)
- Static model list (Claude API doesn't provide list models endpoint)
- Embeddings throw error as Claude doesn't support native embeddings
- Added OpenTelemetry tracing with @TraceLlmCall decorator
- 100% statement, function, and line coverage (79% branch coverage)

Tests:
- Created comprehensive test suite with 20 tests
- All tests follow TDD pattern (written before implementation)
- Tests cover initialization, health checks, chat, streaming, and error handling
- Mocked Anthropic SDK client for isolated unit testing

Quality checks:
- All tests pass (1131 total tests across project)
- ESLint passes with no errors
- TypeScript type checking passes
- Follows existing code patterns from OpenAI and Ollama providers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 14:29:40 -06:00
parent 0fdcfa6ed3
commit 772776bfd9
5 changed files with 820 additions and 0 deletions

32
pnpm-lock.yaml generated
View File

@@ -54,6 +54,9 @@ importers:
apps/api:
dependencies:
'@anthropic-ai/sdk':
specifier: ^0.72.1
version: 0.72.1(zod@4.3.6)
'@mosaic/shared':
specifier: workspace:*
version: link:../../packages/shared
@@ -429,6 +432,15 @@ packages:
'@antfu/install-pkg@1.1.0':
resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
'@anthropic-ai/sdk@0.72.1':
resolution: {integrity: sha512-MiUnue7qN7DvLIoYHgkedN2z05mRf2CutBzjXXY2krzOhG2r/rIfISS2uVkNLikgToB5hYIzw+xp2jdOtRkqYQ==}
hasBin: true
peerDependencies:
zod: ^3.25.0 || ^4.0.0
peerDependenciesMeta:
zod:
optional: true
'@asamuzakjp/css-color@3.2.0':
resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==}
@@ -4393,6 +4405,10 @@ packages:
json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
json-schema-to-ts@3.1.1:
resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==}
engines: {node: '>=16'}
json-schema-traverse@0.4.1:
resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
@@ -5625,6 +5641,9 @@ packages:
resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==}
engines: {node: '>=18'}
ts-algebra@2.0.0:
resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==}
ts-api-utils@2.4.0:
resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==}
engines: {node: '>=18.12'}
@@ -6166,6 +6185,12 @@ snapshots:
package-manager-detector: 1.6.0
tinyexec: 1.0.2
'@anthropic-ai/sdk@0.72.1(zod@4.3.6)':
dependencies:
json-schema-to-ts: 3.1.1
optionalDependencies:
zod: 4.3.6
'@asamuzakjp/css-color@3.2.0':
dependencies:
'@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
@@ -10539,6 +10564,11 @@ snapshots:
json-parse-even-better-errors@2.3.1: {}
json-schema-to-ts@3.1.1:
dependencies:
'@babel/runtime': 7.28.6
ts-algebra: 2.0.0
json-schema-traverse@0.4.1: {}
json-schema-traverse@1.0.0: {}
@@ -11844,6 +11874,8 @@ snapshots:
dependencies:
punycode: 2.3.1
ts-algebra@2.0.0: {}
ts-api-utils@2.4.0(typescript@5.9.3):
dependencies:
typescript: 5.9.3