Skip to content

Commit 0385914

Browse files
committed
rewrite to valibot
1 parent f747c4a commit 0385914

File tree

2 files changed

+142
-72
lines changed

2 files changed

+142
-72
lines changed

packages/mcp-server/src/lib/anthropic.ts

Lines changed: 138 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,116 @@
11
import { Anthropic } from '@anthropic-ai/sdk';
22
import type { Model } from '@anthropic-ai/sdk/resources/messages/messages.js';
3+
import * as v from 'valibot';
34

4-
// Batch API interfaces
5-
export interface SummaryData {
6-
generated_at: string;
7-
model: string;
8-
total_sections: number;
9-
successful_summaries: number;
10-
failed_summaries: number;
11-
summaries: Record<string, string>;
12-
errors?: Array<{ section: string; error: string }>;
13-
download_errors?: Array<{ section: string; error: string }>;
14-
}
5+
// Valibot schemas for Batch API
6+
export const summary_data_schema = v.object({
7+
generated_at: v.string(),
8+
model: v.string(),
9+
total_sections: v.number(),
10+
successful_summaries: v.number(),
11+
failed_summaries: v.number(),
12+
summaries: v.record(v.string(), v.string()),
13+
errors: v.optional(
14+
v.array(
15+
v.object({
16+
section: v.string(),
17+
error: v.string(),
18+
}),
19+
),
20+
),
21+
download_errors: v.optional(
22+
v.array(
23+
v.object({
24+
section: v.string(),
25+
error: v.string(),
26+
}),
27+
),
28+
),
29+
});
1530

16-
// Batch API interfaces
17-
export interface AnthropicBatchRequest {
18-
custom_id: string;
19-
params: {
20-
model: Model;
21-
max_tokens: number;
22-
messages: {
23-
role: 'user' | 'assistant';
24-
content: string | { type: string; text: string }[];
25-
}[];
26-
[key: string]: unknown;
27-
};
28-
}
31+
export const anthropic_batch_request_schema = v.object({
32+
custom_id: v.string(),
33+
params: v.object({
34+
model: v.string(),
35+
max_tokens: v.number(),
36+
messages: v.array(
37+
v.object({
38+
role: v.union([v.literal('user'), v.literal('assistant')]),
39+
content: v.union([
40+
v.string(),
41+
v.array(
42+
v.object({
43+
type: v.string(),
44+
text: v.string(),
45+
}),
46+
),
47+
]),
48+
}),
49+
),
50+
}),
51+
});
2952

30-
export interface AnthropicBatchResponse {
31-
id: string;
32-
type: string;
33-
processing_status: 'in_progress' | 'ended';
34-
request_counts: {
35-
processing: number;
36-
succeeded: number;
37-
errored: number;
38-
canceled: number;
39-
expired: number;
40-
};
41-
ended_at: string | null;
42-
created_at: string;
43-
expires_at: string;
44-
cancel_initiated_at: string | null;
45-
results_url: string | null;
46-
}
53+
export const anthropic_batch_response_schema = v.object({
54+
id: v.string(),
55+
type: v.string(),
56+
processing_status: v.union([v.literal('in_progress'), v.literal('ended')]),
57+
request_counts: v.object({
58+
processing: v.number(),
59+
succeeded: v.number(),
60+
errored: v.number(),
61+
canceled: v.number(),
62+
expired: v.number(),
63+
}),
64+
ended_at: v.nullable(v.string()),
65+
created_at: v.string(),
66+
expires_at: v.string(),
67+
cancel_initiated_at: v.nullable(v.string()),
68+
results_url: v.nullable(v.string()),
69+
});
4770

48-
export interface AnthropicBatchResult {
49-
custom_id: string;
50-
result: {
51-
type: 'succeeded' | 'errored' | 'canceled' | 'expired';
52-
message?: {
53-
id: string;
54-
type: string;
55-
role: string;
56-
model: string;
57-
content: {
58-
type: string;
59-
text: string;
60-
}[];
61-
stop_reason: string;
62-
stop_sequence: string | null;
63-
usage: {
64-
input_tokens: number;
65-
output_tokens: number;
66-
};
67-
};
68-
error?: {
69-
type: string;
70-
message: string;
71-
};
72-
};
73-
}
71+
export const anthropic_batch_result_schema = v.object({
72+
custom_id: v.string(),
73+
result: v.object({
74+
type: v.union([
75+
v.literal('succeeded'),
76+
v.literal('errored'),
77+
v.literal('canceled'),
78+
v.literal('expired'),
79+
]),
80+
message: v.optional(
81+
v.object({
82+
id: v.string(),
83+
type: v.string(),
84+
role: v.string(),
85+
model: v.string(),
86+
content: v.array(
87+
v.object({
88+
type: v.string(),
89+
text: v.string(),
90+
}),
91+
),
92+
stop_reason: v.string(),
93+
stop_sequence: v.nullable(v.string()),
94+
usage: v.object({
95+
input_tokens: v.number(),
96+
output_tokens: v.number(),
97+
}),
98+
}),
99+
),
100+
error: v.optional(
101+
v.object({
102+
type: v.string(),
103+
message: v.string(),
104+
}),
105+
),
106+
}),
107+
});
108+
109+
// Export inferred types
110+
export type SummaryData = v.InferOutput<typeof summary_data_schema>;
111+
export type AnthropicBatchRequest = v.InferOutput<typeof anthropic_batch_request_schema>;
112+
export type AnthropicBatchResponse = v.InferOutput<typeof anthropic_batch_response_schema>;
113+
export type AnthropicBatchResult = v.InferOutput<typeof anthropic_batch_result_schema>;
74114

75115
export class AnthropicProvider {
76116
private client: Anthropic;
@@ -116,7 +156,16 @@ export class AnthropicProvider {
116156
);
117157
}
118158

119-
return await response.json();
159+
const json_data = await response.json();
160+
const validated_response = v.safeParse(anthropic_batch_response_schema, json_data);
161+
162+
if (!validated_response.success) {
163+
throw new Error(
164+
`Invalid batch response from Anthropic API: ${JSON.stringify(validated_response.issues)}`,
165+
);
166+
}
167+
168+
return validated_response.output;
120169
} catch (error) {
121170
console.error('Error creating batch with Anthropic:', error);
122171
throw new Error(
@@ -149,7 +198,16 @@ export class AnthropicProvider {
149198
);
150199
}
151200

152-
return await response.json();
201+
const json_data = await response.json();
202+
const validated_response = v.safeParse(anthropic_batch_response_schema, json_data);
203+
204+
if (!validated_response.success) {
205+
throw new Error(
206+
`Invalid batch status response from Anthropic API: ${JSON.stringify(validated_response.issues)}`,
207+
);
208+
}
209+
210+
return validated_response.output;
153211
} catch (error) {
154212
retry_count++;
155213

@@ -198,12 +256,21 @@ export class AnthropicProvider {
198256

199257
const text = await response.text();
200258
// Parse JSONL format (one JSON object per line)
201-
const results: AnthropicBatchResult[] = text
259+
const parsed_results = text
202260
.split('\n')
203261
.filter((line) => line.trim())
204262
.map((line) => JSON.parse(line));
205263

206-
return results;
264+
// Validate all results
265+
const validated_results = v.safeParse(v.array(anthropic_batch_result_schema), parsed_results);
266+
267+
if (!validated_results.success) {
268+
throw new Error(
269+
`Invalid batch results from Anthropic API: ${JSON.stringify(validated_results.issues)}`,
270+
);
271+
}
272+
273+
return validated_results.output;
207274
} catch (error) {
208275
console.error(`Error getting batch results:`, error);
209276
throw new Error(

packages/mcp-server/src/mcp/utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ export async function get_sections() {
3535
return {
3636
title: section.metadata.title,
3737
use_cases:
38-
section.metadata.use_cases ?? summaries[original_slug] ?? summaries[cleaned_slug] ?? 'use title and path to estimate use case',
38+
section.metadata.use_cases ??
39+
summaries[original_slug] ??
40+
summaries[cleaned_slug] ??
41+
'use title and path to estimate use case',
3942
slug: cleaned_slug,
4043
// Use original slug in URL to ensure it still works
4144
url: `https://svelte.dev/${original_slug}/llms.txt`,

0 commit comments

Comments
 (0)