com um clique
file-uploads
Implementing two-step image upload + LLM pixel call (SEMOSS pattern)
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Menu
Implementing two-step image upload + LLM pixel call (SEMOSS pattern)
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Baseado na classificação ocupacional SOC
Use BEFORE starting a non-trivial task to recall prior lessons, and IMMEDIATELY when the user corrects you, accepts a non-obvious approach, or you hit a subtle gotcha — to persist the lesson for future sessions. Covers SearchMemories, GetMemory, SaveMemoryCandidate, and MarkVerified from the Agent_Memory MCP. Skip entirely if no Agent_Memory MCP is attached to the current app.
Use when compiling, building, or publishing the React app after making changes to source files — at the end of any turn that edited client code, or when the user asks to build, rebuild, or deploy. Invoke the BuildAndPublishApp tool with the project id. Do not attempt to run node, npm, pnpm, or any JavaScript build command via Bash — the sandbox blocks node execution, and BuildAndPublishApp is the only supported build path.
Use when writing code in an app that queries a relational or graph database on the platform, running SELECTs, inserts, updates, deletes, or fetching schema/table structure. Covers the SqlQuery(), SqlQueryBase64(), and GetDatabaseTableStructure() pixel commands via @semoss/sdk's runPixel, plus listing databases with MyEngines(engineTypes=["DATABASE"]). Do not use for LLM calls (see model-engine) or vector database queries.
Use when writing code in an app that calls an LLM, embedding model, or other model engine, OR when listing/selecting models the user has access to. Covers the LLM() and MyEngines() pixel commands via @semoss/sdk's runPixel, including prompt/completion calls, conversational history, image inputs, and parsing model responses. Do not use for vector database queries (see semoss-vector) or guardrail engines (see semoss-guardrail).
Use when writing Python in a SEMOSS app — `py/mcp_driver.py`, helper modules, or any code that runs inside the SEMOSS Python runtime. Covers what SEMOSS injects (`ROOT`, active insight context), what to import from `ai_server` / `semoss` / `smssutil`, the `@mcp_metadata` decorator for exposing MCP tools, and how Python reaches Pixel via `Insight().run_pixel(...)`. For what Pixel commands to actually run, see the `database`, `model`, and `vector` skills. Do not use for Java reactor authoring or frontend `@semoss/sdk` calls.
Use when writing code in an app that creates, lists, renames, pins, or deletes playground rooms, reads/updates room options (model, system prompt, MCPs, temperature), or reads chat history from a room. Covers CreatePlaygroundRoom, GetPlaygroundRooms, GetWorkspaceRooms, RenameRoom, PinRoom, RemoveUserRoom, SetRoomForInsight, GetRoomOptions, UpdateRoomOptions, GetPlaygroundMessages, and AskCOTRoom / COTRoomResult via @semoss/sdk's runPixel. Do not use for LLM completions (see model) or document/vector ingestion (see vector).
| name | file-uploads |
| description | Implementing two-step image upload + LLM pixel call (SEMOSS pattern) |
Overview When a user attaches a file (e.g. an image) to an LLM prompt, the client performs two sequential network calls:
baseUpload — POST the raw file(s) as multipart/form-data to the SEMOSS file-upload endpoint. The backend stores them in the insight workspace and returns a list of { fileName, fileLocation }. Pixel call — POST the chat/LLM request. The fileLocation strings returned by step 1 are embedded as MEDIA parts in the message payload — the file bytes are not re-sent. Always run step 1 to completion before step 2. If step 1 returns no files (or the user attached none), skip straight to step 2 with only a TEXT part.
Step 1 — Upload Endpoint
POST {MODULE}/api/uploadFile/baseUpload?insightId={insightId}&path={encodedPath} MODULE is the SEMOSS base URL (e.g. /Monolith_Dev). insightId is the current insight/session id the LLM call will run under. Required. path is the destination subpath inside the insight workspace; URL-encoded. Empty string ("") is valid and uploads to the workspace root. Body — multipart/form-data with each file appended under the same field name file:
const fd = new FormData(); for (const f of files) fd.append("file", f); // same key for every file Send with credentials: "include" (or whatever your app uses to carry the SEMOSS session cookie). Do not set Content-Type manually — let the browser set the multipart boundary.
Response — JSON array, one entry per file in input order:
{ fileName: string; fileLocation: string }[] fileLocation is the server-side path that the pixel call needs. Persist the array in memory until step 2 completes.
Step 2 — Pixel call (LLM message) Build a parts array. First part is the user's text, then one MEDIA part per uploaded file:
type Part = | { type: "TEXT"; text: string; uiText: string } | { type: "MEDIA"; mediaInfo: { fileName: string; // from upload response fileLocation: string; // from upload response — this is what the backend reads mediaInputType: "FILE"; // literal base64Data?: string; // leave "" — file is already on disk fileFormat?: string; // leave "" mimeType?: string; // leave "" }; };
const parts: Part[] = [{ type: "TEXT", text: prompt, uiText: prompt }]; for (const f of uploaded) { parts.push({ type: "MEDIA", mediaInfo: { base64Data: "", fileFormat: "", fileName: f.fileName, fileLocation: f.fileLocation, mediaInputType: "FILE", mimeType: "", }, }); } Submit parts as the input message to whatever pixel/chat endpoint your app already uses for LLM calls, alongside the same insightId from step 1 and the target modelId/engineId.
Optional: client-side file-type filter If your app has an allow-list of extensions, apply it to the upload response after step 1 (don't block the upload — filter which entries become MEDIA parts). Normalize by lowercasing and stripping a leading dot:
const normalize = (v: string) => v.trim().toLowerCase().replace(/^./, ""); const allowed = new Set(allowedExtensions.map(normalize)); const media = uploaded.filter(f => { const ext = normalize(f.fileName.split(".").pop() ?? ""); return ext && allowed.has(ext); }); Files dropped by this filter are still on the server but won't be referenced by the LLM.
Error handling If baseUpload fails, do not issue the pixel call. Surface the error and let the user retry — preserve the original File objects in component state so the retry doesn't require re-picking them. The upload is non-streaming; await it fully before constructing parts. An empty files array is valid — just skip step 1 and send a text-only pixel call. Reference implementation pointers (SEMOSS repo) Upload helper: libs/sdk/src/api/insight.ts → uploadInsight(insightId, path, files) Orchestration: packages/playground/src/stores/room/room.store.ts → askMessage(prompt, files) (upload → filter → build parts → run pixel call) Part type definitions: packages/playground/src/types.d.ts → PixelMessageTextPart, PixelMessageMediaPart File-picker / drag-drop / paste UI: packages/playground/src/components/room/room-input.tsx