sync: 鍚屾灞€鍩熺綉鏈€鏂颁唬鐮?(source master @ b69568e4)
This commit is contained in:
1 parent
4f7f07504a
commit
8bc8b0e082
1651 files changed
+278599
-11416
No files matched your search
@@ -3,6 +3,7 @@ import type { BaseStream } from "@langchain/langgraph-sdk/react";
|
||||
import { apiFetch } from "../api";
|
||||
import type { AgentThreadState } from "../threads";
|
||||
|
||||
import { resolveStreamingWriteFileArgs } from "./parse-partial-json";
|
||||
import { urlOfArtifact } from "./utils";
|
||||
|
||||
export async function loadArtifactContent({
|
||||
@@ -19,34 +20,34 @@ export async function loadArtifactContent({
|
||||
let enhancedFilepath = filepath;
|
||||
if (filepath.endsWith(".skill")) {
|
||||
enhancedFilepath = filepath + "/SKILL.md";
|
||||
}
|
||||
const url = urlOfArtifact({ filepath: enhancedFilepath, threadId, isMock, publicShareCode });
|
||||
const response = await apiFetch(url);
|
||||
const text = await response.text();
|
||||
if (!response.ok) {
|
||||
let detail = text.trim();
|
||||
try {
|
||||
const parsed = JSON.parse(text) as { detail?: unknown; message?: unknown };
|
||||
const parsedDetail =
|
||||
typeof parsed.detail === "string"
|
||||
? parsed.detail
|
||||
: typeof parsed.message === "string"
|
||||
? parsed.message
|
||||
: "";
|
||||
if (parsedDetail) {
|
||||
detail = parsedDetail;
|
||||
}
|
||||
} catch {
|
||||
// Keep the raw response text as the diagnostic detail.
|
||||
}
|
||||
throw new Error(
|
||||
`Failed to load artifact (${response.status})${detail ? `: ${detail}` : ""}`,
|
||||
);
|
||||
}
|
||||
return { content: text, url };
|
||||
}
|
||||
}
|
||||
const url = urlOfArtifact({ filepath: enhancedFilepath, threadId, isMock, publicShareCode });
|
||||
const response = await apiFetch(url);
|
||||
const text = await response.text();
|
||||
if (!response.ok) {
|
||||
let detail = text.trim();
|
||||
try {
|
||||
const parsed = JSON.parse(text) as { detail?: unknown; message?: unknown };
|
||||
const parsedDetail =
|
||||
typeof parsed.detail === "string"
|
||||
? parsed.detail
|
||||
: typeof parsed.message === "string"
|
||||
? parsed.message
|
||||
: "";
|
||||
if (parsedDetail) {
|
||||
detail = parsedDetail;
|
||||
}
|
||||
} catch {
|
||||
// Keep the raw response text as the diagnostic detail.
|
||||
}
|
||||
throw new Error(
|
||||
`Failed to load artifact (${response.status})${detail ? `: ${detail}` : ""}`,
|
||||
);
|
||||
}
|
||||
return { content: text, url };
|
||||
}
|
||||
|
||||
export function loadArtifactContentFromToolCall({
|
||||
export function loadArtifactContentFromToolCall({
|
||||
url: urlString,
|
||||
thread,
|
||||
}: {
|
||||
@@ -58,66 +59,69 @@ export function loadArtifactContentFromToolCall({
|
||||
const messageId = url.searchParams.get("message_id");
|
||||
if (messageId && toolCallId) {
|
||||
const message = thread.messages.find((message) => message.id === messageId);
|
||||
if (message?.type === "ai" && message.tool_calls) {
|
||||
const toolCall = message.tool_calls.find(
|
||||
(toolCall) => toolCall.id === toolCallId,
|
||||
if (message?.type === "ai") {
|
||||
const toolCall = message.tool_calls?.find(
|
||||
(candidate) => candidate.id === toolCallId,
|
||||
);
|
||||
if (toolCall) {
|
||||
return toolCall.args.content;
|
||||
const { content } = resolveStreamingWriteFileArgs(message, toolCall);
|
||||
if (content) return content;
|
||||
}
|
||||
const chunkOnly = resolveStreamingWriteFileArgs(message, {
|
||||
id: toolCallId,
|
||||
name: "write_file",
|
||||
});
|
||||
if (chunkOnly.content) return chunkOnly.content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeArtifactPath(path: string): string {
|
||||
let normalized = path.trim();
|
||||
const first = normalized[0];
|
||||
const last = normalized.at(-1);
|
||||
if (
|
||||
normalized.length >= 2 &&
|
||||
((first === '"' && last === '"') ||
|
||||
(first === "'" && last === "'") ||
|
||||
(first === "`" && last === "`"))
|
||||
) {
|
||||
normalized = normalized.slice(1, -1).trim();
|
||||
}
|
||||
return normalized.replace(/\\/g, "/").replace(/^\/+/, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Last-resort preview for an output whose artifact HTTP request is unavailable.
|
||||
*
|
||||
* A completed `write_file` can be selected before its backing sandbox file is
|
||||
* visible through the artifacts endpoint (or when a deployment temporarily
|
||||
* serves a stale mount). The original tool arguments still contain the
|
||||
* Markdown, so use the newest matching write as a read-only display fallback.
|
||||
*/
|
||||
export function loadArtifactContentFromLatestWriteFile({
|
||||
filepath,
|
||||
thread,
|
||||
}: {
|
||||
filepath: string;
|
||||
thread: BaseStream<AgentThreadState>;
|
||||
}): string | undefined {
|
||||
const normalizedFilepath = normalizeArtifactPath(filepath);
|
||||
if (!normalizedFilepath) return undefined;
|
||||
|
||||
for (const message of [...thread.messages].reverse()) {
|
||||
if (message.type !== "ai") continue;
|
||||
for (const toolCall of [...(message.tool_calls ?? [])].reverse()) {
|
||||
if (toolCall.name !== "write_file") continue;
|
||||
const args = resolveStreamingWriteFileArgs(message, toolCall);
|
||||
if (
|
||||
args.path &&
|
||||
args.content &&
|
||||
normalizeArtifactPath(args.path) === normalizedFilepath
|
||||
) {
|
||||
return args.content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeArtifactPath(path: string): string {
|
||||
let normalized = path.trim();
|
||||
const first = normalized[0];
|
||||
const last = normalized.at(-1);
|
||||
if (
|
||||
normalized.length >= 2 &&
|
||||
((first === '"' && last === '"') ||
|
||||
(first === "'" && last === "'") ||
|
||||
(first === "`" && last === "`"))
|
||||
) {
|
||||
normalized = normalized.slice(1, -1).trim();
|
||||
}
|
||||
return normalized.replace(/\\/g, "/").replace(/^\/+/, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Last-resort preview for an output whose artifact HTTP request is unavailable.
|
||||
*
|
||||
* A completed `write_file` can be selected before its backing sandbox file is
|
||||
* visible through the artifacts endpoint (or when a deployment temporarily
|
||||
* serves a stale mount). The original tool arguments still contain the
|
||||
* Markdown, so use the newest matching write as a read-only display fallback.
|
||||
*/
|
||||
export function loadArtifactContentFromLatestWriteFile({
|
||||
filepath,
|
||||
thread,
|
||||
}: {
|
||||
filepath: string;
|
||||
thread: BaseStream<AgentThreadState>;
|
||||
}): string | undefined {
|
||||
const normalizedFilepath = normalizeArtifactPath(filepath);
|
||||
if (!normalizedFilepath) return undefined;
|
||||
|
||||
for (const message of [...thread.messages].reverse()) {
|
||||
if (message.type !== "ai" || !message.tool_calls) continue;
|
||||
for (const toolCall of [...message.tool_calls].reverse()) {
|
||||
if (toolCall.name !== "write_file") continue;
|
||||
if (!toolCall.args || typeof toolCall.args !== "object" || Array.isArray(toolCall.args)) {
|
||||
continue;
|
||||
}
|
||||
const args = toolCall.args as { path?: unknown; content?: unknown };
|
||||
if (
|
||||
typeof args.path === "string" &&
|
||||
typeof args.content === "string" &&
|
||||
normalizeArtifactPath(args.path) === normalizedFilepath
|
||||
) {
|
||||
return args.content;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
Reference in new issue
Block a user