Add GLM 5.3 Flash support

This commit is contained in:
Georg Bauer committed 2026-09-01 19:06:20 +02:00
1 parent 9a33c61ea6
commit 46d6a976a5
31 files changed
+7806 -447

No files matched your search

+41 -1
View File
@@ -54,6 +54,23 @@ pub(super) fn anthropic_request(value: Value) -> Result<ChatRequest, (u16, Strin
for block in blocks {
match block.get("type").and_then(Value::as_str).unwrap_or("text") {
"text" => text.push_str(block.get("text").and_then(Value::as_str).unwrap_or("")),
"image" => {
let source = block.get("source").and_then(Value::as_object);
let media = source
.and_then(|source| source.get("media_type"))
.and_then(Value::as_str)
.unwrap_or("");
let data = source
.and_then(|source| source.get("data"))
.and_then(Value::as_str)
.unwrap_or("");
if !matches!(media, "image/png" | "image/jpeg") || data.is_empty() {
return Err((400, "image input must be inline PNG or JPEG base64".into()));
}
text.push_str(&crate::engine::vision_data_marker(&format!(
"data:{media};base64,{data}"
)));
}
"thinking" | "redacted_thinking" => reasoning.push_str(
block
.get("thinking")
@@ -382,6 +399,24 @@ fn responses_content_text(value: &Value) -> Result<String, (u16, String)> {
text.push_str(value);
}
}
Value::Object(part)
if part.get("type").and_then(Value::as_str) == Some("input_image") =>
{
let url = part
.get("image_url")
.or_else(|| part.get("url"))
.and_then(Value::as_str)
.ok_or_else(|| (400, "invalid image input".into()))?;
if !url.starts_with("data:image/png;base64,")
&& !url.starts_with("data:image/jpeg;base64,")
{
return Err((
400,
"image input must be an inline PNG or JPEG data URI".into(),
));
}
text.push_str(&crate::engine::vision_data_marker(url));
}
_ => return Err((400, "invalid JSON request".into())),
}
}
@@ -574,7 +609,12 @@ fn request_reasoning(
|| (explicit_thinking.is_none()
&& matches!(
model_id,
"deepseek-chat" | "glm-5.2-chat" | "glm-5.2-no-think" | "glm-5.2-nothink"
"deepseek-chat"
| "glm-5.2-chat"
| "glm-5.2-no-think"
| "glm-5.2-nothink"
| "glm-5.3-flash-chat"
| "zai/glm-5.3-flash-chat"
))
{
reasoning = ReasoningMode::Direct;
+37 -2
View File
@@ -365,6 +365,7 @@ pub(super) fn render_messages(
let mut turns = Vec::<ChatTurn>::new();
for message in messages {
validate_inline_images(&message.content)?;
let content = content_text(&message.content);
match message.role.as_str() {
"system" | "developer" => {
@@ -426,6 +427,30 @@ pub(super) fn render_messages(
Ok((system, turns))
}
fn validate_inline_images(value: &Value) -> Result<(), (u16, String)> {
let Some(parts) = value.as_array() else {
return Ok(());
};
for part in parts {
let Some(image) = part.get("image_url") else {
continue;
};
let url = image
.get("url")
.or(Some(image))
.and_then(Value::as_str)
.ok_or_else(|| (400, "invalid image input".into()))?;
if !url.starts_with("data:image/png;base64,") && !url.starts_with("data:image/jpeg;base64,")
{
return Err((
400,
"image input must be an inline PNG or JPEG data URI".into(),
));
}
}
Ok(())
}
pub(super) fn validate_tool_results(
state: &State,
messages: &[ApiMessage],
@@ -779,8 +804,18 @@ pub(super) fn content_text(value: &Value) -> String {
Value::Array(parts) => parts
.iter()
.filter_map(|part| match part {
Value::String(text) => Some(text.as_str()),
Value::Object(object) => object.get("text").and_then(Value::as_str),
Value::String(text) => Some(text.clone()),
Value::Object(object) => object
.get("text")
.and_then(Value::as_str)
.map(str::to_owned)
.or_else(|| {
let url = object
.get("image_url")
.and_then(|value| value.get("url").or(Some(value)))
.and_then(Value::as_str)?;
Some(crate::engine::vision_data_marker(url))
}),
_ => None,
})
.collect(),