mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
86 lines
2.1 KiB
86 lines
2.1 KiB
/**
|
|
* Gets the response body from a candidate of the `web_fetch` tool. The model
|
|
* retypes the body, so that JSON can have code fences or text around it. Gives
|
|
* back `undefined` if the candidate has no usable body.
|
|
*/
|
|
export function getWebFetchBody({
|
|
candidate,
|
|
isJson
|
|
}: {
|
|
candidate: string;
|
|
isJson: boolean;
|
|
}): string | undefined {
|
|
const body = candidate.trim();
|
|
|
|
if (!body) {
|
|
return undefined;
|
|
}
|
|
|
|
const json = extractJson(body);
|
|
|
|
if (isWebFetchFailureMessage(json?.value)) {
|
|
return undefined;
|
|
}
|
|
|
|
if (!isJson) {
|
|
return body;
|
|
}
|
|
|
|
return json?.isWhole ? json.text : undefined;
|
|
}
|
|
|
|
/**
|
|
* Gets the error of a failed fetch from a candidate of the `web_fetch` tool.
|
|
* Gives back `undefined` if the candidate has no failure message.
|
|
*/
|
|
export function getWebFetchError({
|
|
candidate
|
|
}: {
|
|
candidate: string;
|
|
}): string | undefined {
|
|
const value = extractJson(candidate.trim())?.value;
|
|
|
|
return isWebFetchFailureMessage(value)
|
|
? (value as { error: string }).error
|
|
: undefined;
|
|
}
|
|
|
|
/**
|
|
* Extracts the JSON object or array from a text. The JSON must start at the
|
|
* beginning of a line and goes to the last matching closing bracket, so that a
|
|
* bracket in a sentence gives no result. `isWhole` tells if only a closing code
|
|
* fence follows the JSON.
|
|
*/
|
|
function extractJson(text: string) {
|
|
const start = text.search(/^[[{]/m);
|
|
|
|
if (start < 0) {
|
|
return undefined;
|
|
}
|
|
|
|
const end = text.lastIndexOf(text[start] === '{' ? '}' : ']') + 1;
|
|
const jsonText = text.slice(start, end);
|
|
|
|
try {
|
|
return {
|
|
isWhole: /^(```)?$/.test(text.slice(end).trim()),
|
|
text: jsonText,
|
|
value: JSON.parse(jsonText) as unknown
|
|
};
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks for the result of a failed fetch, which the model can give back as if
|
|
* it was the response body:
|
|
* { "url": "…", "status": "failed", "error": "HTTP 405: Method Not Allowed" }
|
|
*/
|
|
function isWebFetchFailureMessage(value: unknown) {
|
|
const { error, status, url } = (value ?? {}) as Record<string, unknown>;
|
|
|
|
return (
|
|
status === 'failed' && typeof error === 'string' && typeof url === 'string'
|
|
);
|
|
}
|
|
|