fix: address ds-review-bot v7 findings on the merged image-input head
- gate model selection on steering-placement image carriers from enqueue until their steering/message event publishes; release the gate when an admission ends idle without publication (both behaviorally asserted) - reject session.updateQueue edits carrying non-text blocks at the RPC boundary (queue edits cannot bypass image admission) - extend the durable-directory walk past a first-created DSH_HOME to the deepest pre-existing ancestor - strip Windows-style separators from attachment display names on POSIX - verify attachment reads with a header-only probe (digest already proves the bytes decoded fully at admission); document the read path - make SessionInputShell.addImages refusal observable and keep workspace transfers/composer intake from leaking refused drafts - own ONE recursive image walk (dsh-llm contentHasImage) across apiproxy, pi-ai, compact-basic, and the DeepSeek text-only assertion - drop the redundant canonical-base64 regex and the no-op role read - move AttachmentId/AttachmentError out of types.ts (brand.ts/error.ts); document why AttachmentError does not extend HarnessError - document the hard attachments inject in both consumer READMEs
This commit is contained in:
@@ -33,7 +33,8 @@ import type {
|
||||
import { idleWatchdog, timeoutOf } from '@deepseek-ai/dsh-timeout'
|
||||
import { resolveProfiles } from './config.ts'
|
||||
import type { PiAiProviderProfile, ResolvedPiAiProviderProfile } from './config.ts'
|
||||
import { contentHasImage, toPiContext } from './context.ts'
|
||||
import { contentHasImage } from '@deepseek-ai/dsh-llm'
|
||||
import { toPiContext } from './context.ts'
|
||||
import { toStreamChunks } from './stream.ts'
|
||||
|
||||
/** Constructor options for {@link PiAiAdapter}. */
|
||||
@@ -189,11 +190,7 @@ export class PiAiAdapter extends LlmAdapter {
|
||||
using watchdog = idleWatchdog(upstream, streamIdleTimeoutMs, 'LLM_STREAM_IDLE_TIMEOUT')
|
||||
|
||||
try {
|
||||
const containsImage = options.messages.some((message) => {
|
||||
// The discriminant is part of same-process message validity and is read before content.
|
||||
void message.role
|
||||
return contentHasImage(message.content)
|
||||
})
|
||||
const containsImage = options.messages.some(message => contentHasImage(message.content))
|
||||
if (containsImage && !model.input.includes('image')) {
|
||||
throw new LlmError(`pi-ai model "${model.id}" does not support image input`, 'UNSUPPORTED_CONTENT')
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @module dsh-llm-pi-ai/context
|
||||
*/
|
||||
|
||||
import { CallId, LlmError } from '@deepseek-ai/dsh-llm'
|
||||
import { CallId, contentHasImage, LlmError } from '@deepseek-ai/dsh-llm'
|
||||
import type { ContentBlock, GenerateOptions, Message } from '@deepseek-ai/dsh-llm'
|
||||
import type { AttachmentStore } from '@deepseek-ai/dsh-attachment'
|
||||
import type { Context as PiContext, ImageContent, Message as PiMessage, TextContent, Tool as PiTool } from '@earendil-works/pi-ai'
|
||||
@@ -18,15 +18,6 @@ function flattenText(message: Message): string {
|
||||
.join('')
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether content contains an image, including nested tool results.
|
||||
* @param blocks - content to inspect recursively.
|
||||
* @returns whether any nested block is an image.
|
||||
*/
|
||||
export function contentHasImage(blocks: readonly ContentBlock[]): boolean {
|
||||
return blocks.some(block => block.type === 'image'
|
||||
|| (block.type === 'tool-result' && contentHasImage(block.content)))
|
||||
}
|
||||
|
||||
/** Flatten text recursively inside one tool result. */
|
||||
function toolResultText(blocks: readonly ContentBlock[]): string {
|
||||
|
||||
Reference in New Issue
Block a user