Skip to content

Commit 1ebbc96

Browse files
committed
Match chat attachment guidance to supported file capabilities
1 parent d89bf4e commit 1ebbc96

3 files changed

Lines changed: 91 additions & 5 deletions

File tree

apps/sim/lib/mothership/chat/payload.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ describe('buildCopilotRequestPayload', () => {
518518
content: [
519519
'File "payroll.xlsx" (application/octet-stream, 1 bytes) uploaded to this chat as "uploads/payroll.xlsx" (a chat upload: readable here, not listed under workspace files/).',
520520
'Read it with: sim --output json files read "uploads/payroll.xlsx"',
521-
'Pass the same path "uploads/payroll.xlsx" as inputs.files[].path to mount it in run_code or use it as a reference image in generate_image.',
521+
'Pass the same path "uploads/payroll.xlsx" as inputs.files[].path to mount it in run_code.',
522522
].join('\n'),
523523
},
524524
])
@@ -559,7 +559,7 @@ describe('buildCopilotRequestPayload', () => {
559559
type: 'uploaded_file',
560560
content: [
561561
'File "photo.png" (image/png, 10 bytes) uploaded to this chat as "uploads/photo.png" (a chat upload: readable here, not listed under workspace files/).',
562-
'Read it with: sim --output json files read "uploads/photo.png"',
562+
'Inspect it with sim_cli: {"args":["files","view","uploads/photo.png"]}',
563563
'Pass the same path "uploads/photo.png" as inputs.files[].path to mount it in run_code or use it as a reference image in generate_image.',
564564
].join('\n'),
565565
},
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { buildUploadedFileContext } from '@/lib/mothership/chat/upload-context'
3+
4+
describe('uploaded file discovery guidance', () => {
5+
it.each([
6+
['photo.png', 'image/png'],
7+
['photo.jpeg', 'image/jpeg'],
8+
['photo.webp', 'image/webp'],
9+
['photo.gif', 'image/gif'],
10+
['report.pdf', 'application/pdf'],
11+
['photo.png', 'image/png; charset=binary'],
12+
])('routes supported visual %s through the agent vision command', (name, mediaType) => {
13+
const { content } = buildUploadedFileContext(name, mediaType, 10)
14+
15+
expect(content).toContain(
16+
`Inspect it with sim_cli: ${JSON.stringify({ args: ['files', 'view', `uploads/${name}`] })}`
17+
)
18+
expect(content).not.toContain('files read')
19+
expect(content).toContain('inputs.files[].path to mount it in run_code')
20+
if (mediaType === 'application/pdf') expect(content).not.toContain('reference image')
21+
})
22+
23+
it.each([
24+
['notes.txt', 'text/plain'],
25+
['report.md', 'text/markdown'],
26+
['payroll.xlsx', 'application/octet-stream'],
27+
])('retains text extraction for supported %s', (name, mediaType) => {
28+
const { content } = buildUploadedFileContext(name, mediaType, 10)
29+
30+
expect(content).toContain(`files read "uploads/${name}"`)
31+
expect(content).not.toContain('files","view')
32+
expect(content).not.toContain('reference image')
33+
})
34+
35+
it.each([
36+
['clip.mp4', 'video/mp4'],
37+
['audio.mp3', 'audio/mpeg'],
38+
['drawing.svg', 'image/svg+xml'],
39+
['scan.tiff', 'image/tiff'],
40+
['unknown.bin', 'application/octet-stream'],
41+
])('offers a code mount without unsupported read/view instructions for %s', (name, mediaType) => {
42+
const { content } = buildUploadedFileContext(name, mediaType, 10)
43+
44+
expect(content).toContain(`"uploads/${name}" as inputs.files[].path to mount it in run_code.`)
45+
expect(content).not.toContain('files read')
46+
expect(content).not.toContain('files","view')
47+
expect(content).not.toContain('reference image')
48+
})
49+
50+
it('keeps the same encoded reference in vision and code guidance', () => {
51+
const { content } = buildUploadedFileContext('screen shot #1.png', 'image/png', 10)
52+
53+
expect(content).toContain('"args":["files","view","uploads/screen%20shot%20%231.png"]')
54+
expect(content).toContain('"uploads/screen%20shot%20%231.png" as inputs.files[].path')
55+
})
56+
57+
it('retains workflow JSON import guidance', () => {
58+
const { content } = buildUploadedFileContext('workflow.json', 'application/json', 10)
59+
60+
expect(content).toContain('files read "uploads/workflow.json"')
61+
expect(content).toContain('workflows import --workflow')
62+
})
63+
64+
it('retains archive extraction without suggesting direct reads or vision', () => {
65+
const { content } = buildUploadedFileContext('bundle.zip', 'application/zip', 10)
66+
67+
expect(content).toContain('Archive "bundle.zip"')
68+
expect(content).toContain('unzip it there')
69+
expect(content).not.toContain('Read it with:')
70+
expect(content).not.toContain('Inspect it with sim_cli:')
71+
})
72+
})

apps/sim/lib/mothership/chat/upload-context.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
import { isSupportedFileType } from '@/lib/file-parsers'
2+
import { ObservationMediaType } from '@/lib/mothership/generated/observations'
13
import type { ChatContextItem } from '@/lib/mothership/generated/protocol'
24
import { encodeVfsSegment } from '@/lib/mothership/vfs/path-utils'
3-
import { buildArchiveExtractGuidance, isArchiveFileName } from '@/lib/uploads/utils/file-utils'
5+
import {
6+
buildArchiveExtractGuidance,
7+
getFileExtension,
8+
isArchiveFileName,
9+
} from '@/lib/uploads/utils/file-utils'
410

511
/** The same chat upload path works for CLI reads, code mounts and image references. */
612
export function buildUploadedFileContext(
@@ -23,11 +29,19 @@ export function buildUploadedFileContext(
2329
].join('\n'),
2430
}
2531
}
32+
const reference = `uploads/${encodedUploadName}`
33+
const visualType = ObservationMediaType.safeParse(mediaType.split(';')[0]?.trim())
2634
const lines = [
2735
`File "${displayName}" (${mediaType}, ${size} bytes) uploaded to this chat as "uploads/${encodedUploadName}" (a chat upload: readable here, not listed under workspace files/).`,
28-
`Read it with: sim --output json files read "uploads/${encodedUploadName}"`,
29-
`Pass the same path "uploads/${encodedUploadName}" as inputs.files[].path to mount it in run_code or use it as a reference image in generate_image.`,
3036
]
37+
if (visualType.success) {
38+
lines.push(`Inspect it with sim_cli: ${JSON.stringify({ args: ['files', 'view', reference] })}`)
39+
} else if (isSupportedFileType(getFileExtension(displayName))) {
40+
lines.push(`Read it with: sim --output json files read "${reference}"`)
41+
}
42+
lines.push(
43+
`Pass the same path "${reference}" as inputs.files[].path to mount it in run_code${visualType.success && visualType.data.startsWith('image/') ? ' or use it as a reference image in generate_image' : ''}.`
44+
)
3145
if (displayName.endsWith('.json')) {
3246
lines.push(
3347
"If it is a workflow export: read it with files read, then import the JSON with: sim --output json workflows import --workflow '<the JSON>'"

0 commit comments

Comments
 (0)