Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e794e60
fix(provider): trim whitespace and filter empty content in transforma…
jerome-benoit Jan 6, 2026
4e90386
refactor: align variable naming
jerome-benoit Jan 6, 2026
56664e3
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 6, 2026
6da9d4f
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 6, 2026
3bd13e9
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 6, 2026
9f60d44
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 7, 2026
e85f9e6
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 7, 2026
375137d
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 16, 2026
1fc41f8
fix(test): add missing options parameter to ProviderTransform.message…
jerome-benoit Jan 16, 2026
10dffd8
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 17, 2026
3d9f057
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 17, 2026
821c383
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 19, 2026
ad988cd
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 23, 2026
14567ba
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 25, 2026
688b9d6
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 26, 2026
9ee9f00
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 26, 2026
d33f5e9
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 27, 2026
6f03580
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Jan 31, 2026
e12dc8c
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Feb 2, 2026
f31b1a0
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Feb 3, 2026
452414b
Merge branch 'dev' into fix/provider-whitespace-trimming
jerome-benoit Feb 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 27 additions & 17 deletions packages/console/app/src/routes/zen/util/provider/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ export function fromAnthropicRequest(body: any): CommonRequest {
if (!s) continue
if ((s as any).type !== "text") continue
if (typeof (s as any).text !== "string") continue
if ((s as any).text.length === 0) continue
msgs.push({ role: "system", content: (s as any).text })
if ((s as any).text.trim().length === 0) continue
msgs.push({ role: "system", content: (s as any).text.trim() })
}
}

Expand Down Expand Up @@ -221,8 +221,10 @@ export function fromAnthropicRequest(body: any): CommonRequest {
const partsOut: any[] = []
for (const p of partsIn) {
if (!p || !(p as any).type) continue
if ((p as any).type === "text" && typeof (p as any).text === "string")
partsOut.push({ type: "text", text: (p as any).text })
if ((p as any).type === "text" && typeof (p as any).text === "string") {
const t = (p as any).text.trim()
if (t.length > 0) partsOut.push({ type: "text", text: t })
}
if ((p as any).type === "image") {
const ip = toImg((p as any).source)
if (ip) partsOut.push(ip)
Expand All @@ -247,7 +249,10 @@ export function fromAnthropicRequest(body: any): CommonRequest {
const tcs: any[] = []
for (const p of partsIn) {
if (!p || !(p as any).type) continue
if ((p as any).type === "text" && typeof (p as any).text === "string") texts.push((p as any).text)
if ((p as any).type === "text" && typeof (p as any).text === "string") {
const t = (p as any).text.trim()
if (t.length > 0) texts.push(t)
}
if ((p as any).type === "tool_use") {
const name = (p as any).name
const id = (p as any).id
Expand Down Expand Up @@ -324,8 +329,8 @@ export function toAnthropicRequest(body: CommonRequest) {
return ccCount <= 4 ? { cache_control: { type: "ephemeral" } } : {}
}
const system = sysIn
.filter((m: any) => typeof m.content === "string" && m.content.length > 0)
.map((m: any) => ({ type: "text", text: m.content, ...cc() }))
.filter((m: any) => typeof m.content === "string" && m.content.trim().length > 0)
.map((m: any) => ({ type: "text", text: m.content.trim(), ...cc() }))

const msgsIn = Array.isArray(body.messages) ? body.messages : []
const msgsOut: any[] = []
Expand All @@ -348,16 +353,21 @@ export function toAnthropicRequest(body: CommonRequest) {

if ((m as any).role === "user") {
if (typeof (m as any).content === "string") {
msgsOut.push({
role: "user",
content: [{ type: "text", text: (m as any).content, ...cc() }],
})
const c = (m as any).content.trim()
if (c.length > 0) {
msgsOut.push({
role: "user",
content: [{ type: "text", text: c, ...cc() }],
})
}
} else if (Array.isArray((m as any).content)) {
const parts: any[] = []
for (const p of (m as any).content) {
if (!p || !(p as any).type) continue
if ((p as any).type === "text" && typeof (p as any).text === "string")
parts.push({ type: "text", text: (p as any).text, ...cc() })
if ((p as any).type === "text" && typeof (p as any).text === "string") {
const t = (p as any).text.trim()
if (t.length > 0) parts.push({ type: "text", text: t, ...cc() })
}
if ((p as any).type === "image_url") {
const s = toSrc(p)
if (s) parts.push({ type: "image", source: s, ...cc() })
Expand All @@ -370,8 +380,8 @@ export function toAnthropicRequest(body: CommonRequest) {

if ((m as any).role === "assistant") {
const out: any = { role: "assistant", content: [] as any[] }
if (typeof (m as any).content === "string" && (m as any).content.length > 0) {
;(out.content as any[]).push({ type: "text", text: (m as any).content, ...cc() })
if (typeof (m as any).content === "string" && (m as any).content.trim().length > 0) {
;(out.content as any[]).push({ type: "text", text: (m as any).content.trim(), ...cc() })
}
if (Array.isArray((m as any).tool_calls)) {
for (const tc of (m as any).tool_calls) {
Expand Down Expand Up @@ -554,8 +564,8 @@ export function toAnthropicResponse(resp: CommonResponse) {

const content: any[] = []

if (typeof message.content === "string" && message.content.length > 0)
content.push({ type: "text", text: message.content })
if (typeof message.content === "string" && message.content.trim().length > 0)
content.push({ type: "text", text: message.content.trim() })

if (Array.isArray(message.tool_calls)) {
for (const tc of message.tool_calls) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,18 +82,22 @@ export function fromOaCompatibleRequest(body: any): CommonRequest {
if (!m || !m.role) continue

if (m.role === "system") {
if (typeof m.content === "string" && m.content.length > 0) msgsOut.push({ role: "system", content: m.content })
if (typeof m.content === "string" && m.content.trim().length > 0)
msgsOut.push({ role: "system", content: m.content.trim() })
continue
}

if (m.role === "user") {
if (typeof m.content === "string") {
msgsOut.push({ role: "user", content: m.content })
if (typeof m.content === "string" && m.content.trim().length > 0) {
msgsOut.push({ role: "user", content: m.content.trim() })
} else if (Array.isArray(m.content)) {
const parts: any[] = []
for (const p of m.content) {
if (!p || !p.type) continue
if (p.type === "text" && typeof p.text === "string") parts.push({ type: "text", text: p.text })
if (p.type === "text" && typeof p.text === "string") {
const t = p.text.trim()
if (t.length > 0) parts.push({ type: "text", text: t })
}
if (p.type === "image_url") parts.push({ type: "image_url", image_url: p.image_url })
}
if (parts.length === 1 && parts[0].type === "text") msgsOut.push({ role: "user", content: parts[0].text })
Expand All @@ -104,7 +108,7 @@ export function fromOaCompatibleRequest(body: any): CommonRequest {

if (m.role === "assistant") {
const out: any = { role: "assistant" }
if (typeof m.content === "string") out.content = m.content
if (typeof m.content === "string" && m.content.trim().length > 0) out.content = m.content.trim()
if (Array.isArray(m.tool_calls)) out.tool_calls = m.tool_calls
msgsOut.push(out)
continue
Expand Down Expand Up @@ -150,20 +154,24 @@ export function toOaCompatibleRequest(body: CommonRequest) {
if (!m || !m.role) continue

if (m.role === "system") {
if (typeof m.content === "string" && m.content.length > 0) msgsOut.push({ role: "system", content: m.content })
if (typeof m.content === "string" && m.content.trim().length > 0)
msgsOut.push({ role: "system", content: m.content.trim() })
continue
}

if (m.role === "user") {
if (typeof m.content === "string") {
msgsOut.push({ role: "user", content: m.content })
if (typeof m.content === "string" && m.content.trim().length > 0) {
msgsOut.push({ role: "user", content: m.content.trim() })
continue
}
if (Array.isArray(m.content)) {
const parts: any[] = []
for (const p of m.content) {
if (!p || !p.type) continue
if (p.type === "text" && typeof p.text === "string") parts.push({ type: "text", text: p.text })
if (p.type === "text" && typeof p.text === "string") {
const t = p.text.trim()
if (t.length > 0) parts.push({ type: "text", text: t })
}
const ip = toImg(p)
if (ip) parts.push(ip)
}
Expand All @@ -175,7 +183,7 @@ export function toOaCompatibleRequest(body: CommonRequest) {

if (m.role === "assistant") {
const out: any = { role: "assistant" }
if (typeof m.content === "string") out.content = m.content
if (typeof m.content === "string" && m.content.trim().length > 0) out.content = m.content.trim()
if (Array.isArray(m.tool_calls)) out.tool_calls = m.tool_calls
msgsOut.push(out)
continue
Expand Down Expand Up @@ -225,8 +233,8 @@ export function fromOaCompatibleResponse(resp: any): CommonResponse {

const content: any[] = []

if (typeof message.content === "string" && message.content.length > 0) {
content.push({ type: "text", text: message.content })
if (typeof message.content === "string" && message.content.trim().length > 0) {
content.push({ type: "text", text: message.content.trim() })
}

if (Array.isArray(message.tool_calls)) {
Expand Down
42 changes: 24 additions & 18 deletions packages/console/app/src/routes/zen/util/provider/openai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,24 +115,25 @@ export function fromOpenaiRequest(body: any): CommonRequest {

if ((m as any).role === "system" || (m as any).role === "developer") {
const c = (m as any).content
if (typeof c === "string" && c.length > 0) msgs.push({ role: "system", content: c })
if (typeof c === "string" && c.trim().length > 0) msgs.push({ role: "system", content: c.trim() })
if (Array.isArray(c)) {
const t = c.find((p: any) => p && typeof p.text === "string")
if (t && typeof t.text === "string" && t.text.length > 0) msgs.push({ role: "system", content: t.text })
if (t && typeof t.text === "string" && t.text.trim().length > 0)
msgs.push({ role: "system", content: t.text.trim() })
}
continue
}

if ((m as any).role === "user") {
const c = (m as any).content
if (typeof c === "string") {
msgs.push({ role: "user", content: c })
} else if (Array.isArray(c)) {
if (typeof c === "string" && c.trim().length > 0) msgs.push({ role: "user", content: c.trim() })
else if (Array.isArray(c)) {
const parts: any[] = []
for (const p of c) {
if (!p || !(p as any).type) continue
if (((p as any).type === "text" || (p as any).type === "input_text") && typeof (p as any).text === "string")
parts.push({ type: "text", text: (p as any).text })
if (((p as any).type === "text" || (p as any).type === "input_text") && typeof (p as any).text === "string") {
if ((p as any).text.trim().length > 0) parts.push({ type: "text", text: (p as any).text.trim() })
}
const ip = toImg(p)
if (ip) parts.push(ip)
if ((p as any).type === "tool_result") {
Expand All @@ -151,7 +152,7 @@ export function fromOpenaiRequest(body: any): CommonRequest {
if ((m as any).role === "assistant") {
const c = (m as any).content
const out: any = { role: "assistant" }
if (typeof c === "string" && c.length > 0) out.content = c
if (typeof c === "string" && c.trim().length > 0) out.content = c.trim()
if (Array.isArray((m as any).tool_calls)) out.tool_calls = (m as any).tool_calls
msgs.push(out)
continue
Expand Down Expand Up @@ -206,8 +207,11 @@ export function toOpenaiRequest(body: CommonRequest) {

const toPart = (p: any) => {
if (!p || typeof p !== "object") return undefined
if ((p as any).type === "text" && typeof (p as any).text === "string")
return { type: "input_text", text: (p as any).text }
if ((p as any).type === "text" && typeof (p as any).text === "string") {
const t = (p as any).text.trim()
if (t.length === 0) return undefined
return { type: "input_text", text: t }
}
if ((p as any).type === "image_url" && (p as any).image_url)
return { type: "input_image", image_url: (p as any).image_url }
const s = (p as any).source
Expand All @@ -231,15 +235,17 @@ export function toOpenaiRequest(body: CommonRequest) {

if ((m as any).role === "system") {
const c = (m as any).content
if (typeof c === "string") input.push({ role: "system", content: c })
if (typeof c === "string" && c.trim().length > 0) input.push({ role: "system", content: c.trim() })
continue
}

if ((m as any).role === "user") {
const c = (m as any).content
if (typeof c === "string") {
input.push({ role: "user", content: [{ type: "input_text", text: c }] })
} else if (Array.isArray(c)) {
if (typeof c === "string" && c.trim().length > 0) {
input.push({ role: "user", content: [{ type: "input_text", text: c.trim() }] })
continue
}
if (Array.isArray(c)) {
const parts: any[] = []
for (const p of c) {
const op = toPart(p)
Expand All @@ -252,8 +258,8 @@ export function toOpenaiRequest(body: CommonRequest) {

if ((m as any).role === "assistant") {
const c = (m as any).content
if (typeof c === "string" && c.length > 0) {
input.push({ role: "assistant", content: [{ type: "output_text", text: c }] })
if (typeof c === "string" && c.trim().length > 0) {
input.push({ role: "assistant", content: [{ type: "output_text", text: c.trim() }] })
}
if (Array.isArray((m as any).tool_calls)) {
for (const tc of (m as any).tool_calls) {
Expand Down Expand Up @@ -417,13 +423,13 @@ export function toOpenaiResponse(resp: CommonResponse) {

const outputItems: any[] = []

if (typeof msg.content === "string" && msg.content.length > 0) {
if (typeof msg.content === "string" && msg.content.trim().length > 0) {
outputItems.push({
id: `msg_${Math.random().toString(36).slice(2)}`,
type: "message",
status: "completed",
role: "assistant",
content: [{ type: "output_text", text: msg.content, annotations: [], logprobs: [] }],
content: [{ type: "output_text", text: msg.content.trim(), annotations: [], logprobs: [] }],
})
}

Expand Down
31 changes: 22 additions & 9 deletions packages/opencode/src/provider/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,33 @@ export namespace ProviderTransform {
msgs = msgs
.map((msg) => {
if (typeof msg.content === "string") {
if (msg.content === "") return undefined
return msg
if (msg.content.trim().length === 0) return undefined
return { ...msg, content: msg.content.trim() }
}
if (!Array.isArray(msg.content)) return msg
const filtered = msg.content.filter((part) => {
if (part.type === "text" || part.type === "reasoning") {
return part.text !== ""
}
return true
})
const filtered = msg.content
.filter((part) => {
if (part.type === "text" || part.type === "reasoning") {
if (typeof (part as any).text === "string") return (part as any).text.trim().length > 0
return false
}
return true
})
.map((part) => {
if ((part.type === "text" || part.type === "reasoning") && typeof (part as any).text === "string") {
return { ...part, text: (part as any).text.trim() }
}
return part
})
if (filtered.length === 0) return undefined
return { ...msg, content: filtered }
})
.filter((msg): msg is ModelMessage => msg !== undefined && msg.content !== "")
.filter((msg): msg is ModelMessage => {
if (msg === undefined) return false
if (typeof msg.content === "string") return msg.content.length > 0
if (Array.isArray(msg.content)) return msg.content.length > 0
return true
})
}

if (model.api.id.includes("claude")) {
Expand Down
Loading
Loading