AIX: Anthropic: Search/Fetch - done

NOTEs: this works without saving the server-side tool invocation and the subsequent responses
to AIX particles, and consequently to DMessageFragments of the opportune type.
-> Shall do it with execution graph fragments.
This commit is contained in:
Enrico Ros
2025-10-18 14:51:21 -07:00
parent 8984b65a51
commit 66382ed980
3 changed files with 48 additions and 3 deletions
@@ -154,7 +154,7 @@ export function aixToAnthropicMessageCreate(model: AixAPI_Model, _chatGenerate:
hostedTools.push({
type: 'web_search_20250305',
name: 'web_search',
max_uses: 5, // Allow up to 5 progressive searches
max_uses: 10, // Allow up to 10 progressive searches // FIXME: HARDCODED
});
}
@@ -26,6 +26,7 @@ import { AnthropicWire_API_Message_Create } from '../../wiretypes/anthropic.wire
* - 'input_json_delta': Partial JSON strings for tool_use and server_tool_use inputs.
* - 'thinking_delta': Incremental thinking content updates.
* - 'signature_delta': Signature for thinking blocks.
* - 'citations_delta': Citations that stream incrementally for text blocks.
*
* Client Tools vs Server Tools
*
@@ -122,6 +123,7 @@ export function createAnthropicMessageParser(): ChatGenerateParseFunction {
switch (content_block.type) {
case 'text':
pt.appendText(content_block.text);
// Note: In streaming mode, citations arrive via citations_delta events, not on content_block_start
break;
case 'thinking':
@@ -274,6 +276,27 @@ export function createAnthropicMessageParser(): ChatGenerateParseFunction {
throw new Error('Unexpected signature delta');
break;
case 'citations_delta':
// Citations arrive incrementally during streaming - add to current text block
if (contentBlock.type === 'text') {
const citation = delta.citation;
if (citation.type === 'web_search_result_location') {
// Web search citation from server-side search
pt.appendUrlCitation(
citation.title || citation.url,
citation.url,
undefined, // citationNumber
undefined, // startIndex
undefined, // endIndex
citation.cited_text, // textSnippet
undefined // pubTs
);
}
// TODO: Handle other citation types (char_location, page_location, content_block_location, search_result_location)
} else
throw new Error('Unexpected citations_delta on non-text block');
break;
// note: redacted_thinking doesn't have deltas, only start (with payload) and stop
default:
@@ -368,6 +391,23 @@ export function createAnthropicMessageParserNS(): ChatGenerateParseFunction {
switch (contentBlock.type) {
case 'text':
pt.appendText(contentBlock.text);
// Handle citations if present (non-streaming mode has all citations attached)
if (contentBlock.citations && Array.isArray(contentBlock.citations)) {
for (const citation of contentBlock.citations) {
if (citation.type === 'web_search_result_location') {
pt.appendUrlCitation(
citation.title || citation.url,
citation.url,
undefined, // citationNumber
undefined, // startIndex
undefined, // endIndex
citation.cited_text, // textSnippet
undefined // pubTs
);
}
// TODO: Handle other citation types (char_location, page_location, content_block_location, search_result_location)
}
}
break;
case 'thinking':
@@ -42,7 +42,7 @@ export namespace AnthropicWire_Blocks {
});
/** Citations schema used in both input and output. Output includes a file_id field for document citations, which input will omit. */
const _TextBlockCitations_schema = z.discriminatedUnion('type', [
export const _TextBlockCitations_schema = z.discriminatedUnion('type', [
// PDF citation (page location)
z.object({
type: z.literal('page_location'),
@@ -73,7 +73,7 @@ export namespace AnthropicWire_Blocks {
end_block_index: z.number(),
file_id: z.string().nullish(), // Present in response only
}),
// Web search result citation
// Web search result citation - produced by the hosted web_search tool
z.object({
type: z.literal('web_search_result_location'),
cited_text: z.string(),
@@ -848,6 +848,11 @@ export namespace AnthropicWire_API_Message_Create {
type: z.literal('signature_delta'),
signature: z.string(),
}),
z.object({
// created by the hosted web_search tool, at least, in which case the citation is: Extract<typeof _TextBlockCitations_schema, { type: 'web_search_result_location' }>
type: z.literal('citations_delta'),
citation: AnthropicWire_Blocks._TextBlockCitations_schema,
}),
]),
});