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.
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[];}
pub struct SourcePart { /// The URL or identifier of the document. pub source: String, /// The title of the document. pub title: String, /// The content of the document. pub content: Vec<Part>,}
type SourcePart struct { // The URL or identifier of the document. Source string `json:"source"` // The title of the document. Title string `json:"title"` // The content of the document. Content []Part `json:"content"`}
When a model returns an attributed answer it adds Citation
entries to the generated TextPart
.
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;}
pub struct TextPart { pub text: String, #[serde(skip_serializing_if = "Option::is_none")] pub citations: Option<Vec<Citation>>,}
pub struct Citation { /** * The URL or identifier of the document being cited. */ pub source: String, /** * The title of the document being cited. */ #[serde(skip_serializing_if = "Option::is_none")] pub title: Option<String>, /** * The text snippet from the document being cited. */ #[serde(skip_serializing_if = "Option::is_none")] pub cited_text: Option<String>, /** * The start index of the document content part being cited. */ pub start_index: usize, /** * The end index of the document content part being cited. */ pub end_index: usize,}
type TextPart struct { Text string `json:"text"` Citations []Citation `json:"citations,omitempty"`}
type Citation struct { // The URL or identifier of the document being cited. Source string `json:"source"` // The title of the document being cited. Title *string `json:"title,omitempty"` // The text snippet from the document being cited. CitedText *string `json:"cited_text,omitempty"` // The start index of the document content part being cited. StartIndex int `json:"start_index"` // The end index of the document content part being cited. EndIndex int `json:"end_index"`}
Before we look at concrete requests, it helps to understand how citation spans
break large passages into multiple TextPart
s.
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 TextPart
s contain Citation
entries that point to the relevant source documents. The other TextPart
s contain only text.
Generate citations
Section titled “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.
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 });
use dotenvy::dotenv;use llm_sdk::{LanguageModelInput, Message, Part, UserMessage};use serde_json::json;
mod common;
#[tokio::main]async fn main() { dotenv().ok();
let model = common::get_model("anthropic", "claude-opus-4-20250514");
let response = model .generate(LanguageModelInput { messages: vec![ Message::User(UserMessage { content: vec![ // Provide sources as part of the user message Part::source( "https://health-site.example/articles/coffee-benefits", "Coffee Health Benefits: What the Research Shows", vec![Part::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.", )], ), Part::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.", ), ], }), Message::assistant(vec![ // The model requests a tool call to get more data, which includes sources Part::tool_call( "caffeine_lookup_456", "lookup", json!({ "query": "caffeine sensitivity optimal timing metabolism coffee health benefits" }), ), ]), Message::tool(vec![ Part::tool_result( "caffeine_lookup_456", "lookup", vec![ // Provide other sources as part of the tool result Part::source( "https://medical-journal.example/2024/caffeine-metabolism-study", "Optimizing Coffee Intake for Caffeine-Sensitive Individuals", vec![Part::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.", )], ), ], ), ]), ], ..Default::default() }) .await .unwrap();
println!("{:#?}", response.content);}
package main
import ( "context" "log"
llmsdk "github.com/hoangvvo/llm-sdk/sdk-go" "github.com/hoangvvo/llm-sdk/sdk-go/examples" "github.com/sanity-io/litter")
func main() { model := examples.GetModel("anthropic", "claude-opus-4-20250514")
response, err := model.Generate(context.Background(), &llmsdk.LanguageModelInput{ Messages: []llmsdk.Message{ llmsdk.NewUserMessage( // Provide sources as part of the user message llmsdk.NewSourcePart( "https://health-site.example/articles/coffee-benefits", "Coffee Health Benefits: What the Research Shows", []llmsdk.Part{llmsdk.NewTextPart( "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.", ), }, ), llmsdk.NewTextPart( "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.", ), ), llmsdk.NewAssistantMessage( // The model requests a tool call to get more data, which includes sources llmsdk.NewToolCallPart( "caffeine_lookup_456", "lookup", map[string]any{ "query": "caffeine sensitivity optimal timing metabolism coffee health benefits", }, ), ), llmsdk.NewToolMessage( llmsdk.NewToolResultPart( "caffeine_lookup_456", "lookup", []llmsdk.Part{ // Provide other sources as part of the tool result llmsdk.NewSourcePart( "https://medical-journal.example/2024/caffeine-metabolism-study", "Optimizing Coffee Intake for Caffeine-Sensitive Individuals", []llmsdk.Part{llmsdk.NewTextPart( "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.", ), }, ), }, false, ), ), }, })
if err != nil { log.Fatalf("Generation failed: %v", err) }
litter.Dump(response.Content)}
Stream citations
Section titled “Stream citations”During streaming, citation information arrives incrementally as CitationDelta
objects nested inside TextPartDelta
updates.
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;}
pub struct TextPartDelta { pub text: String, #[serde(skip_serializing_if = "Option::is_none")] pub citation: Option<CitationDelta>,}
pub struct CitationDelta { /// The type of the citation delta. #[serde(rename = "type")] pub r#type: String, /// The URL or identifier of the document being cited. #[serde(skip_serializing_if = "Option::is_none")] pub source: Option<String>, /// The title of the document being cited. #[serde(skip_serializing_if = "Option::is_none")] pub title: Option<String>, /// The text snippet from the document being cited. #[serde(skip_serializing_if = "Option::is_none")] pub cited_text: Option<String>, /// The start index of the document content part being cited. #[serde(skip_serializing_if = "Option::is_none")] pub start_index: Option<usize>, /// The end index of the document content part being cited. #[serde(skip_serializing_if = "Option::is_none")] pub end_index: Option<usize>,}
type TextPartDelta struct { Text string `json:"text"` Citation *CitationDelta `json:"citation,omitempty"`}
type CitationDelta struct { Type string `json:"type"` Source *string `json:"source,omitempty"` Title *string `json:"title,omitempty"` CitedText *string `json:"cited_text,omitempty"` StartIndex *int `json:"start_index,omitempty"` EndIndex *int `json:"end_index,omitempty"`}
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.
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 });
use dotenvy::dotenv;use futures::stream::StreamExt;use llm_sdk::{LanguageModelInput, Message, Part, StreamAccumulator, UserMessage};use serde_json::json;
mod common;
#[tokio::main]async fn main() { dotenv().ok();
let mut stream = common::get_model("anthropic", "claude-opus-4-20250514") .stream(LanguageModelInput { messages: vec![ Message::User(UserMessage { content: vec![ // Provide sources as part of the user message Part::source( "https://health-site.example/articles/coffee-benefits", "Coffee Health Benefits: What the Research Shows", vec![Part::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.", )], ), Part::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.", ), ], }), Message::assistant(vec![ // The model requests a tool call to get more data, which includes sources Part::tool_call( "caffeine_lookup_456", "lookup", json!({ "query": "caffeine sensitivity optimal timing metabolism coffee health benefits" }), ), ]), Message::tool(vec![ Part::tool_result( "caffeine_lookup_456", "lookup", vec![ // Provide other sources as part of the tool result Part::source( "https://medical-journal.example/2024/caffeine-metabolism-study", "Optimizing Coffee Intake for Caffeine-Sensitive Individuals", vec![Part::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.", )], ), ], ), ]), ], ..Default::default() }) .await .unwrap();
let mut accumulator = StreamAccumulator::new();
while let Some(partial) = stream.next().await { let partial = partial.unwrap(); println!("{partial:#?}"); accumulator.add_partial(partial).unwrap(); }
let final_response = accumulator.compute_response(); println!("{final_response:#?}");}
package main
import ( "context" "log"
llmsdk "github.com/hoangvvo/llm-sdk/sdk-go" "github.com/hoangvvo/llm-sdk/sdk-go/examples" "github.com/sanity-io/litter")
func main() { model := examples.GetModel("anthropic", "claude-opus-4-20250514")
stream, err := model.Stream(context.Background(), &llmsdk.LanguageModelInput{ Messages: []llmsdk.Message{ llmsdk.NewUserMessage( // Provide sources as part of the user message llmsdk.NewSourcePart( "https://health-site.example/articles/coffee-benefits", "Coffee Health Benefits: What the Research Shows", []llmsdk.Part{llmsdk.NewTextPart( "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.", ), }, ), llmsdk.NewTextPart( "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.", ), ), llmsdk.NewAssistantMessage( // The model requests a tool call to get more data, which includes sources llmsdk.NewToolCallPart( "caffeine_lookup_456", "lookup", map[string]any{ "query": "caffeine sensitivity optimal timing metabolism coffee health benefits", }, ), ), llmsdk.NewToolMessage( llmsdk.NewToolResultPart( "caffeine_lookup_456", "lookup", []llmsdk.Part{ // Provide other sources as part of the tool result llmsdk.NewSourcePart( "https://medical-journal.example/2024/caffeine-metabolism-study", "Optimizing Coffee Intake for Caffeine-Sensitive Individuals", []llmsdk.Part{llmsdk.NewTextPart( "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.", ), }, ), }, false, ), ), }, })
if err != nil { log.Fatalf("Stream failed: %v", err) }
accumulator := llmsdk.NewStreamAccumulator()
for stream.Next() { current := stream.Current() litter.Dump(current)
if err := accumulator.AddPartial(*current); err != nil { log.Printf("Failed to add partial: %v", err) } }
if err := stream.Err(); err != nil { log.Fatalf("Stream error: %v", err) }
finalResponse, err := accumulator.ComputeResponse() if err != nil { log.Fatalf("Failed to compute response: %v", err) }
litter.Dump(finalResponse.Content)}