Skip to content

Citations

Citation-enabled models can attribute parts of their responses back to specific source documents. The SDK represents this with SourcePart inputs and Citation metadata that appears alongside text output.

SourcePart carries the structured document content the model can cite. It can be added directly to the conversation history or returned from tools.

types.ts
interface SourcePart {
type: "source";
/**
* The URL or identifier of the document.
*/
source: string;
/**
* The title of the document.
*/
title: string;
/**
* The content of the document.
*/
content: Part[];
}

When a model returns an attributed answer it adds Citation entries to the generated TextPart.

types.ts
interface TextPart {
type: "text";
text: string;
citations?: Citation[];
}
interface Citation {
/**
* The URL or identifier of the document being cited.
*/
source: string;
/**
* The title of the document being cited.
*/
title?: string;
/**
* The text snippet from the document being cited.
*/
cited_text?: string;
/**
* The start index of the document content part being cited.
*/
start_index: number;
/**
* The end index of the document content part being cited.
*/
end_index: number;
}

Before we look at concrete requests, it helps to understand how citation spans break large passages into multiple TextParts.

flowchart TB
  A["[ [Text] ] Outline the routine by writing a single intention for the day,"]
  B["[ [Text]  [Citation] ] noting that a three-minute box-breathing warm-up can lower cortisol swings by 15%. (some-website.com/breathing-study)"]
  C["[ [Text] ] Next, recommend stepping outside for ten minutes of natural light to cue wakefulness,"]
  D["[ [Text]  [Citation] ] a practice linked to earlier sleep times in longitudinal tracking data, (some-website.com/light-benefits)"]
  E["[ [Text] ] and finish by blocking a 45-minute deep-work sprint before opening messengers."]

  A --> B --> C --> D --> E

In this case, 2 of the TextParts contain Citation entries that point to the relevant source documents. The other TextParts contain only text.

generate-citations

Include SourcePart entries in your messages (either directly from the user or via tool outputs) so the model can ground its response. Each source needs a stable source identifier or URL, a human-readable title, and the document content broken into structured parts.

Use generate() when you want a single response that includes citations. The SDK returns TextPart objects whose citations array points to the matching source identifiers and spans within your provided documents.

generate-citations.ts
import { getModel } from "./get-model.ts";
const model = getModel("anthropic", "claude-opus-4-20250514");
const response = await model.generate({
messages: [
{
role: "user",
content: [
// Provide sources as part of the user message
{
type: "source",
source: "https://health-site.example/articles/coffee-benefits",
title: "Coffee Health Benefits: What the Research Shows",
content: [
{
type: "text",
text: [
"Coffee contains over 1,000 bioactive compounds, with caffeine being the most studied.",
"A typical 8-ounce cup contains 80-100mg of caffeine.",
"Research shows moderate coffee consumption (3-4 cups daily) is associated with reduced risk of type 2 diabetes, Parkinson's disease, and liver disease.",
"The antioxidants in coffee, particularly chlorogenic acid, may contribute to these protective effects beyond just the caffeine content.",
].join(" "),
},
],
},
{
type: "text",
text: [
"Based on what you know about coffee's health benefits and caffeine content,",
"what would be the optimal daily coffee consumption for someone who wants the health benefits but is sensitive to caffeine?",
"Consider timing and metabolism.",
].join(" "),
},
],
},
{
role: "assistant",
content: [
// The model requests a tool call to get more data, which includes sources
{
type: "tool-call",
tool_call_id: "caffeine_lookup_456",
tool_name: "lookup",
args: {
query:
"caffeine sensitivity optimal timing metabolism coffee health benefits",
},
},
],
},
{
role: "tool",
content: [
{
type: "tool-result",
tool_name: "lookup",
tool_call_id: "caffeine_lookup_456",
// Provide other sources as part of the tool result
content: [
{
type: "source",
source:
"https://medical-journal.example/2024/caffeine-metabolism-study",
title:
"Optimizing Coffee Intake for Caffeine-Sensitive Individuals",
content: [
{
type: "text",
text: [
"For caffeine-sensitive individuals, the half-life of caffeine extends to 8-12 hours compared to the average 5-6 hours.",
"These individuals experience effects at doses as low as 50mg.",
"Research shows consuming 1-2 cups (100-200mg caffeine) before noon provides 75% of coffee's antioxidant benefits while minimizing side effects like insomnia and anxiety.",
"Splitting intake into smaller doses (half-cups) throughout the morning can further reduce sensitivity reactions while maintaining beneficial compound levels.",
].join(" "),
},
],
},
],
},
],
},
],
});
console.dir(response.content, { depth: null });

During streaming, citation information arrives incrementally as CitationDelta objects nested inside TextPartDelta updates.

types.ts
interface TextPartDelta {
type: "text";
text: string;
citation?: CitationDelta;
}
interface CitationDelta {
type: "citation";
/**
* The URL or identifier of the document being cited.
*/
source?: string;
/**
* The title of the document being cited.
*/
title?: string;
/**
* The text snippet from the document being cited.
*/
cited_text?: string;
/**
* The start index of the document content part being cited.
*/
start_index?: number;
/**
* The end index of the document content part being cited.
*/
end_index?: number;
}

When you stream responses, TextPartDelta chunks include optional citation deltas. Accumulate the stream with the provided StreamAccumulator so citations and text are merged safely in arrival order.

stream-citations.ts
import { StreamAccumulator } from "@hoangvvo/llm-sdk";
import { getModel } from "./get-model.ts";
const model = getModel("anthropic", "claude-opus-4-20250514");
const stream = await model.stream({
messages: [
{
role: "user",
content: [
// Provide sources as part of the user message
{
type: "source",
source: "https://health-site.example/articles/coffee-benefits",
title: "Coffee Health Benefits: What the Research Shows",
content: [
{
type: "text",
text: [
"Coffee contains over 1,000 bioactive compounds, with caffeine being the most studied.",
"A typical 8-ounce cup contains 80-100mg of caffeine.",
"Research shows moderate coffee consumption (3-4 cups daily) is associated with reduced risk of type 2 diabetes, Parkinson's disease, and liver disease.",
"The antioxidants in coffee, particularly chlorogenic acid, may contribute to these protective effects beyond just the caffeine content.",
].join(" "),
},
],
},
{
type: "text",
text: [
"Based on what you know about coffee's health benefits and caffeine content,",
"what would be the optimal daily coffee consumption for someone who wants the health benefits but is sensitive to caffeine?",
"Consider timing and metabolism.",
].join(" "),
},
],
},
{
role: "assistant",
content: [
// The model requests a tool call to get more data, which includes sources
{
type: "tool-call",
tool_call_id: "caffeine_lookup_456",
tool_name: "lookup",
args: {
query:
"caffeine sensitivity optimal timing metabolism coffee health benefits",
},
},
],
},
{
role: "tool",
content: [
{
type: "tool-result",
tool_name: "lookup",
tool_call_id: "caffeine_lookup_456",
// Provide other sources as part of the tool result
content: [
{
type: "source",
source:
"https://medical-journal.example/2024/caffeine-metabolism-study",
title:
"Optimizing Coffee Intake for Caffeine-Sensitive Individuals",
content: [
{
type: "text",
text: [
"For caffeine-sensitive individuals, the half-life of caffeine extends to 8-12 hours compared to the average 5-6 hours.",
"These individuals experience effects at doses as low as 50mg.",
"Research shows consuming 1-2 cups (100-200mg caffeine) before noon provides 75% of coffee's antioxidant benefits while minimizing side effects like insomnia and anxiety.",
"Splitting intake into smaller doses (half-cups) throughout the morning can further reduce sensitivity reactions while maintaining beneficial compound levels.",
].join(" "),
},
],
},
],
},
],
},
],
});
const accumulator = new StreamAccumulator();
for await (const partial of stream) {
console.dir(partial, { depth: null });
accumulator.addPartial(partial);
}
const response = accumulator.computeResponse();
console.dir(response.content, { depth: null });