1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//! Provider-facing message types.
//!
//! `LanguageModelPrompt` is the standardized prompt format that providers receive
//! in `CallOptions.prompt`. It is converted from `ModelMessage` (user-facing)
//! by the `generate_text` / `stream_text` functions.
use crate::content::ContentPart;
use crate::message::Role;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use ts_rs::TS;
/// A single provider-facing message.
///
/// Unlike `ModelMessage` (which can have string content), this always has
/// `content: Vec<ContentPart>` — strings are normalized to `ContentPart::Text`.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, TS)]
#[ts(export)]
pub struct LanguageModelPromptMessage {
pub role: Role,
pub content: Vec<ContentPart>,
/// Message-level provider-specific options (e.g.
/// `anthropic.cacheControl`), applied to the last part of the message when
/// converting to a provider prompt. Mirrors the Vercel AI SDK
/// `LanguageModelV4Message.providerOptions`.
pub provider_options: Option<Value>,
}
/// The standardized prompt passed to `LanguageModel::do_generate` / `do_stream`.
pub type LanguageModelPrompt = Vec<LanguageModelPromptMessage>;
/// Convert user-facing `ModelMessage`s into a `LanguageModelPrompt`.
///
/// - String content is normalized to a single `ContentPart::Text`.
/// - If `instructions` is provided, it is prepended as a system message.
#[must_use]
pub fn convert_to_language_model_prompt(
messages: &[crate::message::ModelMessage],
instructions: Option<&str>,
) -> LanguageModelPrompt {
let mut result = Vec::new();
// Prepend system instructions if provided.
if let Some(instructions) = instructions {
result.push(LanguageModelPromptMessage {
role: Role::System,
content: vec![ContentPart::text(instructions)],
provider_options: None,
});
}
for msg in messages {
let parts = match &msg.content {
crate::message::MessageContent::Text(text) => vec![ContentPart::text(text)],
crate::message::MessageContent::Parts(parts) => parts.clone(),
};
result.push(LanguageModelPromptMessage {
role: msg.role,
content: parts,
provider_options: None,
});
}
result
}