app-server: expose image-context tri-state

This commit is contained in:
Colin Young
2026-02-10 13:03:57 -08:00
parent a6e9469fa4
commit 8db0700334
20 changed files with 733 additions and 22 deletions
+30
View File
@@ -72,6 +72,12 @@ pub enum ContentItem {
OutputText { text: String },
}
impl ContentItem {
pub fn is_input_image(&self) -> bool {
matches!(self, ContentItem::InputImage { .. })
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
/// Classifies an assistant message as interim commentary or final answer text.
@@ -195,6 +201,24 @@ pub enum ResponseItem {
Other,
}
impl ResponseItem {
pub fn has_input_image(&self) -> bool {
match self {
ResponseItem::Message { content, .. } => {
content.iter().any(ContentItem::is_input_image)
}
ResponseItem::FunctionCallOutput { output, .. } => {
output.content_items().is_some_and(|items| {
items
.iter()
.any(FunctionCallOutputContentItem::is_input_image)
})
}
_ => false,
}
}
}
pub const BASE_INSTRUCTIONS_DEFAULT: &str = include_str!("prompts/base_instructions/default.md");
/// Base instructions for the model in a thread. Corresponds to the `instructions` field in the ResponsesAPI.
@@ -795,6 +819,12 @@ pub enum FunctionCallOutputContentItem {
InputImage { image_url: String },
}
impl FunctionCallOutputContentItem {
pub fn is_input_image(&self) -> bool {
matches!(self, FunctionCallOutputContentItem::InputImage { .. })
}
}
/// Converts structured function-call output content into plain text for
/// human-readable surfaces.
///
+29
View File
@@ -72,6 +72,35 @@ pub enum InputModality {
Image,
}
pub const INPUT_MODALITY_TEXT_MASK: i64 = 1;
pub const INPUT_MODALITY_IMAGE_MASK: i64 = 1 << 1;
pub fn input_modality_to_mask(modality: InputModality) -> i64 {
match modality {
InputModality::Text => INPUT_MODALITY_TEXT_MASK,
InputModality::Image => INPUT_MODALITY_IMAGE_MASK,
}
}
pub fn input_modalities_to_mask(modalities: &[InputModality]) -> i64 {
modalities
.iter()
.copied()
.map(input_modality_to_mask)
.fold(0, |mask, bit| mask | bit)
}
pub fn input_modalities_from_mask(mask: i64) -> Vec<InputModality> {
let mut modalities = Vec::new();
if mask & INPUT_MODALITY_TEXT_MASK != 0 {
modalities.push(InputModality::Text);
}
if mask & INPUT_MODALITY_IMAGE_MASK != 0 {
modalities.push(InputModality::Image);
}
modalities
}
/// Backward-compatible default when `input_modalities` is omitted on the wire.
///
/// Legacy payloads predate modality metadata, so we conservatively assume both text and images are