@ykurilov/agent-chat-core 0.57.0 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/dist/{chunk-BS2HDKZB.js → chunk-QA5DVEXF.js} +699 -56
- package/dist/chunk-QA5DVEXF.js.map +1 -0
- package/dist/index.js +57 -1
- package/dist/index.js.map +1 -1
- package/dist/storage-contract.js +1 -1
- package/dist/types/agent-run/build-model-request.d.ts +6 -0
- package/dist/types/agent-run/forged-blocks.d.ts +117 -0
- package/dist/types/agent-run/next-attempt.d.ts +12 -1
- package/dist/types/agent-run/run-agent-turn.d.ts +12 -0
- package/dist/types/attachments/message-attachments.d.ts +3 -0
- package/dist/types/chat-service.d.ts +26 -0
- package/dist/types/compaction/compaction-service.d.ts +3 -0
- package/dist/types/compaction/run-compaction.d.ts +3 -0
- package/dist/types/helpers/run-helper.d.ts +3 -0
- package/dist/types/index.d.ts +5 -1
- package/dist/types/memory/long-term-memory.d.ts +3 -0
- package/dist/types/provider-errors/classify-provider-error.d.ts +5 -0
- package/dist/types/provider-errors/error-signs.d.ts +2 -2
- package/dist/types/provider-errors/provider-outages.d.ts +41 -0
- package/dist/types/storage/postgres-migrations.d.ts +28 -0
- package/dist/types/text/markdown-code.d.ts +6 -0
- package/package.json +1 -1
- package/dist/chunk-BS2HDKZB.js.map +0 -1
|
@@ -1853,6 +1853,494 @@ function createBurstWaits() {
|
|
|
1853
1853
|
};
|
|
1854
1854
|
}
|
|
1855
1855
|
|
|
1856
|
+
// src/agent-run/forged-blocks.ts
|
|
1857
|
+
import { wrapLanguageModel as wrapLanguageModel2 } from "ai";
|
|
1858
|
+
|
|
1859
|
+
// src/text/markdown-code.ts
|
|
1860
|
+
function codeFenceOf(line) {
|
|
1861
|
+
return /^ {0,3}(`{3,}|~{3,})/.exec(line)?.[1] ?? null;
|
|
1862
|
+
}
|
|
1863
|
+
function closesCodeFence(line, fence) {
|
|
1864
|
+
const opener = codeFenceOf(line);
|
|
1865
|
+
return opener !== null && opener[0] === fence[0] && opener.length >= fence.length && line.trim() === opener;
|
|
1866
|
+
}
|
|
1867
|
+
function isIndentedCodeOrQuote(line) {
|
|
1868
|
+
return /^(?: {4}|\t)/.test(line) || /^\s*>/.test(line);
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
// src/agent-run/length-cut.ts
|
|
1872
|
+
var LENGTH_CUT_PART_TYPE = "data-length-cut";
|
|
1873
|
+
var LENGTH_CUT_NOTE = "[\u041E\u0442\u0432\u0435\u0442 \u043E\u0431\u043E\u0440\u0432\u0430\u043D: \u043C\u043E\u0434\u0435\u043B\u044C \u0443\u043F\u0451\u0440\u043B\u0430\u0441\u044C \u0432 \u043F\u0440\u0435\u0434\u0435\u043B \u0434\u043B\u0438\u043D\u044B \u043E\u0442\u0432\u0435\u0442\u0430]";
|
|
1874
|
+
function lengthCutForModel(part) {
|
|
1875
|
+
return part.type === LENGTH_CUT_PART_TYPE ? { type: "text", text: LENGTH_CUT_NOTE } : void 0;
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// src/agent-run/unanswered-messages.ts
|
|
1879
|
+
var UNANSWERED_NOTE = "[\u041E\u0442\u0432\u0435\u0442\u0430 \u043D\u0430 \u044D\u0442\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043D\u0435\u0442: \u0445\u043E\u0434 \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0441\u044F \u0434\u043E \u043F\u0435\u0440\u0432\u044B\u0445 \u0441\u043B\u043E\u0432]";
|
|
1880
|
+
function withUnansweredMarked(messages) {
|
|
1881
|
+
return messages.flatMap(
|
|
1882
|
+
(message, index) => message.role === "user" && messages[index - 1]?.role === "user" ? [{ role: "assistant", content: [{ type: "text", text: UNANSWERED_NOTE }] }, message] : [message]
|
|
1883
|
+
);
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1886
|
+
// src/agent-run/forged-blocks.ts
|
|
1887
|
+
var CHANNEL_NOTE_LINES = [
|
|
1888
|
+
"\u0420\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0441\u043D\u043E\u0432\u0430 \u0432 \u0432\u0435\u0431-\u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0438: \u043E\u0431\u044B\u0447\u043D\u0430\u044F \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0430 \u0438 \u0438\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B \u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B.",
|
|
1889
|
+
"\u0420\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0438\u0434\u0451\u0442 \u0432 Telegram. \u041F\u0438\u0448\u0438 \u043E\u0431\u044B\u0447\u043D\u044B\u043C Markdown: \u0435\u0433\u043E \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0443\u0442 \u0432 \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0443 Telegram. \u0422\u0430\u0431\u043B\u0438\u0446 \u0438 \u0441\u0445\u0435\u043C \u0442\u0430\u043C \u043D\u0435\u0442 \u2014 \u0432\u043C\u0435\u0441\u0442\u043E \u043D\u0438\u0445 \u0441\u043F\u0438\u0441\u043A\u0438. \u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043A\u043E\u0440\u043E\u0447\u0435, \u0447\u0435\u043C \u0432 \u0432\u0435\u0431\u0435.",
|
|
1890
|
+
"\u0420\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0438\u0434\u0451\u0442 \u0432\u043E \u0412\u041A\u043E\u043D\u0442\u0430\u043A\u0442\u0435. \u0420\u0430\u0437\u043C\u0435\u0442\u043A\u0438 \u0442\u0430\u043C \u043D\u0435\u0442: Markdown \u043F\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442\u0441\u044F \u0432 \u043F\u0440\u043E\u0441\u0442\u043E\u0439 \u0442\u0435\u043A\u0441\u0442. \u041F\u0438\u0448\u0438 \u043F\u0440\u043E\u0441\u0442\u044B\u043C \u0442\u0435\u043A\u0441\u0442\u043E\u043C \u0438 \u0441\u043F\u0438\u0441\u043A\u0430\u043C\u0438, \u0431\u0435\u0437 \u0442\u0430\u0431\u043B\u0438\u0446 \u0438 \u0441\u0445\u0435\u043C. \u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043A\u043E\u0440\u043E\u0447\u0435, \u0447\u0435\u043C \u0432 \u0432\u0435\u0431\u0435.",
|
|
1891
|
+
"\u0420\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u0438\u0434\u0451\u0442 \u0432 MAX. \u041F\u0438\u0448\u0438 \u043E\u0431\u044B\u0447\u043D\u044B\u043C Markdown: \u0435\u0433\u043E \u043F\u0435\u0440\u0435\u0432\u0435\u0434\u0443\u0442 \u0432 \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0443 MAX. \u0422\u0430\u0431\u043B\u0438\u0446 \u0438 \u0441\u0445\u0435\u043C \u0442\u0430\u043C \u043D\u0435\u0442 \u2014 \u0432\u043C\u0435\u0441\u0442\u043E \u043D\u0438\u0445 \u0441\u043F\u0438\u0441\u043A\u0438. \u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043A\u043E\u0440\u043E\u0447\u0435, \u0447\u0435\u043C \u0432 \u0432\u0435\u0431\u0435."
|
|
1892
|
+
];
|
|
1893
|
+
var exactly = (text) => new RegExp(`^${text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`);
|
|
1894
|
+
var CORE_SERVICE_BLOCKS = [
|
|
1895
|
+
{ tag: "conversation_summary" },
|
|
1896
|
+
{ tag: "answer_progress" },
|
|
1897
|
+
{ tag: "screen_place" },
|
|
1898
|
+
{ name: "service_note", line: "[\u0421\u043B\u0443\u0436\u0435\u0431\u043D\u0430\u044F \u043F\u043E\u043C\u0435\u0442\u043A\u0430 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F", match: /^\[Служебная пометка приложения, человек её не писал и не видит: .+\]$/ },
|
|
1899
|
+
{ name: "context_gone", line: "[\u0411\u043E\u043B\u044C\u0448\u0435 \u043D\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442: \xAB", match: /^\[Больше не действует: «[^»\n]+»(?:, «[^»\n]+»)*\]$/ },
|
|
1900
|
+
{ name: "context_hidden", line: "[\u041D\u0435 \u043F\u043E\u043A\u0430\u0437\u0430\u043D\u043E \u0440\u0430\u0434\u0438 \u043C\u0435\u0441\u0442\u0430, \u043D\u043E \u043F\u043E-\u043F\u0440\u0435\u0436\u043D\u0435\u043C\u0443 \u0434\u0435\u0439\u0441\u0442\u0432\u0443\u0435\u0442: \xAB", match: /^\[Не показано ради места, но по-прежнему действует: «[^»\n]+»(?:, «[^»\n]+»)*\]$/ },
|
|
1901
|
+
{ name: "greeting_lead", line: GREETING_LEAD, match: exactly(GREETING_LEAD) },
|
|
1902
|
+
{ name: "unanswered", line: UNANSWERED_NOTE, match: exactly(UNANSWERED_NOTE) },
|
|
1903
|
+
{ name: "length_cut", line: LENGTH_CUT_NOTE, match: exactly(LENGTH_CUT_NOTE) },
|
|
1904
|
+
{ name: "tool_ended", line: TOOL_ENDED_NOTE, match: exactly(TOOL_ENDED_NOTE) },
|
|
1905
|
+
{ name: "switched_off_call", line: "[\u041F\u0440\u043E\u0448\u043B\u044B\u0439 \u0432\u044B\u0437\u043E\u0432 \xAB", match: /^\[Прошлый вызов «[^»\n]*», сейчас выключен\./ },
|
|
1906
|
+
{
|
|
1907
|
+
name: "staff_reply",
|
|
1908
|
+
line: "\u0416\u0438\u0432\u043E\u0439 \u0441\u043E\u0442\u0440\u0443\u0434\u043D\u0438\u043A",
|
|
1909
|
+
match: /^Живой сотрудник(?: «[^»\n]*»)? ответил человеку вместо тебя: «.*»\. Это сказано от вашего имени: продолжай разговор с учётом этого и не противоречь сотруднику\.$/
|
|
1910
|
+
},
|
|
1911
|
+
...CHANNEL_NOTE_LINES.map((note) => ({ name: "channel_note", line: note, match: exactly(note) })),
|
|
1912
|
+
// Имя автора — коротко и без знаков конца фразы: «Сообщение в группе от участника: Аня».
|
|
1913
|
+
{ name: "group_author", line: "\u0421\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432 \u0433\u0440\u0443\u043F\u043F\u0435 \u043E\u0442 \u0443\u0447\u0430\u0441\u0442\u043D\u0438\u043A\u0430: ", match: /^Сообщение в группе от участника: [^\s.!?:;][^.!?:;\n]{0,79}$/ }
|
|
1914
|
+
];
|
|
1915
|
+
var serviceBlockName = (format) => "tag" in format ? format.tag : format.name;
|
|
1916
|
+
function explainsServiceFormats(question2, formats) {
|
|
1917
|
+
const text = question2.toLowerCase();
|
|
1918
|
+
if (formats.some((format) => "tag" in format && text.includes(format.tag.toLowerCase()))) return true;
|
|
1919
|
+
return /(?:^|[^\p{L}])(?:тег(?:а|е|и|у|ом|ов|ами|ам|ах)?|формат(?:а|е|у|ом|ы|ов|ами|ам|ах)?|разметк[аеиу]|tags?|formats?|markup)(?:[^\p{L}]|$)/u.test(text);
|
|
1920
|
+
}
|
|
1921
|
+
var EXAMPLE_LEAD = /(?:^|[^\p{L}])(?:например|пример\p{L}*|образец|шаблон\p{L}*|выгляд\p{L}*|формат\p{L}*|такой|такая|такое|такие|так|example|e\.g\.|looks like|format)(?:[^\p{L}][^\n]*)?:\s*$/iu;
|
|
1922
|
+
var textOfMessage = (message) => message.parts.map((part) => part.type === "text" ? part.text : "").join("\n");
|
|
1923
|
+
function lastQuestionOf(history) {
|
|
1924
|
+
const question2 = history.findLast((message) => message.role === "user");
|
|
1925
|
+
return question2 ? textOfMessage(question2) : "";
|
|
1926
|
+
}
|
|
1927
|
+
function checkServiceBlockFormats(formats) {
|
|
1928
|
+
for (const format of formats) {
|
|
1929
|
+
const shown = JSON.stringify(format);
|
|
1930
|
+
if (!format || typeof format !== "object") throw new TypeError(`serviceBlocks: \u043D\u0443\u0436\u0435\u043D \u043E\u0431\u044A\u0435\u043A\u0442 \u0444\u043E\u0440\u043C\u0430\u0442\u0430, \u0430 \u043D\u0435 ${shown}`);
|
|
1931
|
+
if ("tag" in format) {
|
|
1932
|
+
if (typeof format.tag !== "string" || !/^[A-Za-z][\w:.-]*$/.test(format.tag)) throw new TypeError(`serviceBlocks: \u0438\u043C\u044F \u0442\u0435\u0433\u0430 \u2014 \u043B\u0430\u0442\u0438\u043D\u0438\u0446\u0430, \u0446\u0438\u0444\u0440\u044B, _ : . -, \u0430 \u043D\u0435 ${shown}`);
|
|
1933
|
+
continue;
|
|
1934
|
+
}
|
|
1935
|
+
if (typeof format.name !== "string" || format.name === "") throw new TypeError(`serviceBlocks: \u0443 \u0441\u043B\u0443\u0436\u0435\u0431\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0438 \u043D\u0443\u0436\u043D\u043E \u0438\u043C\u044F \u0434\u043B\u044F \u0436\u0443\u0440\u043D\u0430\u043B\u0430: ${shown}`);
|
|
1936
|
+
if (typeof format.line !== "string" || format.line.trim() === "") throw new TypeError(`serviceBlocks: \u0443 \u0441\u043B\u0443\u0436\u0435\u0431\u043D\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0438 \u043D\u0443\u0436\u043D\u043E \u043D\u0435\u043F\u0443\u0441\u0442\u043E\u0435 \u043D\u0430\u0447\u0430\u043B\u043E line: ${shown}`);
|
|
1937
|
+
if (format.match !== void 0 && !(format.match instanceof RegExp)) throw new TypeError(`serviceBlocks: match \u2014 \u0440\u0435\u0433\u0443\u043B\u044F\u0440\u043D\u043E\u0435 \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u0435: ${format.name}`);
|
|
1938
|
+
if (format.until !== void 0 && format.until !== "paragraph" && !(format.until instanceof RegExp)) {
|
|
1939
|
+
throw new TypeError(`serviceBlocks: until \u2014 "paragraph" \u0438\u043B\u0438 \u0440\u0435\u0433\u0443\u043B\u044F\u0440\u043D\u043E\u0435 \u0432\u044B\u0440\u0430\u0436\u0435\u043D\u0438\u0435: ${format.name}`);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
var compiledByList = /* @__PURE__ */ new WeakMap();
|
|
1944
|
+
function compile(formats) {
|
|
1945
|
+
const known = compiledByList.get(formats);
|
|
1946
|
+
if (known) return known;
|
|
1947
|
+
const tags = formats.flatMap((format) => "tag" in format ? [format.tag] : []);
|
|
1948
|
+
const lines = formats.filter((format) => !("tag" in format));
|
|
1949
|
+
const escape = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1950
|
+
const starts = [...tags.map((tag) => `<\\/?${escape(tag)}`), ...lines.map((format) => escape(format.line))];
|
|
1951
|
+
const hint = starts.length > 0 ? new RegExp(`(?:^|\\n) {0,3}(?:${starts.join("|")})`) : /(?!)/;
|
|
1952
|
+
const compiled = { tags, lines, hint };
|
|
1953
|
+
compiledByList.set(formats, compiled);
|
|
1954
|
+
return compiled;
|
|
1955
|
+
}
|
|
1956
|
+
var MAX_ATTRIBUTES_LENGTH = 200;
|
|
1957
|
+
function paragraphEnd(text) {
|
|
1958
|
+
const blank = /\n[ \t]*(?:\n|$)/g;
|
|
1959
|
+
blank.lastIndex = Math.max(0, text.indexOf("\n"));
|
|
1960
|
+
const found = text.indexOf("\n") < 0 ? null : blank.exec(text);
|
|
1961
|
+
return found ? found.index + 1 : -1;
|
|
1962
|
+
}
|
|
1963
|
+
function createForgedBlockScanner(formats, options2 = {}) {
|
|
1964
|
+
const compiled = compile(formats);
|
|
1965
|
+
const lines = compiled.lines;
|
|
1966
|
+
const tags = options2.explaining ? [] : compiled.tags;
|
|
1967
|
+
let fence = null;
|
|
1968
|
+
let previousLine = "";
|
|
1969
|
+
let pending = "";
|
|
1970
|
+
let ordinaryLine = null;
|
|
1971
|
+
let block = null;
|
|
1972
|
+
let skipSpace = false;
|
|
1973
|
+
let out = [];
|
|
1974
|
+
const emitText = (text) => {
|
|
1975
|
+
if (!text) return;
|
|
1976
|
+
const last = out.at(-1);
|
|
1977
|
+
if (last?.kind === "text") last.text += text;
|
|
1978
|
+
else out.push({ kind: "text", text });
|
|
1979
|
+
};
|
|
1980
|
+
const emitGap = (text) => {
|
|
1981
|
+
const last = out.at(-1);
|
|
1982
|
+
if (last?.kind === "gap") last.text += text;
|
|
1983
|
+
else out.push({ kind: "gap", text });
|
|
1984
|
+
};
|
|
1985
|
+
const closeOrdinaryLine = (line) => {
|
|
1986
|
+
if (line.trim() !== "") previousLine = line;
|
|
1987
|
+
if (fence !== null) {
|
|
1988
|
+
if (closesCodeFence(line, fence)) fence = null;
|
|
1989
|
+
} else {
|
|
1990
|
+
const opener = codeFenceOf(line);
|
|
1991
|
+
if (opener) fence = opener;
|
|
1992
|
+
}
|
|
1993
|
+
};
|
|
1994
|
+
function classify(line, complete) {
|
|
1995
|
+
if (fence !== null || isIndentedCodeOrQuote(line) || codeFenceOf(line)) return "text";
|
|
1996
|
+
const indent = /^ {0,3}/.exec(line)[0].length;
|
|
1997
|
+
const body = line.slice(indent);
|
|
1998
|
+
if (body === "") return complete ? "text" : "wait";
|
|
1999
|
+
let waiting = false;
|
|
2000
|
+
if (body[0] === "<") {
|
|
2001
|
+
const closing = body[1] === "/";
|
|
2002
|
+
const nameAt = closing ? 2 : 1;
|
|
2003
|
+
for (const tag of tags) {
|
|
2004
|
+
const head = body.slice(nameAt, nameAt + tag.length);
|
|
2005
|
+
if (!tag.startsWith(head)) continue;
|
|
2006
|
+
if (head.length < tag.length) {
|
|
2007
|
+
waiting ||= body.length === nameAt + head.length;
|
|
2008
|
+
continue;
|
|
2009
|
+
}
|
|
2010
|
+
if (closing) continue;
|
|
2011
|
+
const next = body[nameAt + tag.length];
|
|
2012
|
+
if (next === void 0) {
|
|
2013
|
+
waiting = true;
|
|
2014
|
+
continue;
|
|
2015
|
+
}
|
|
2016
|
+
if (next !== ">" && !/\s/.test(next)) continue;
|
|
2017
|
+
const end = body.indexOf(">", nameAt + tag.length);
|
|
2018
|
+
if (end >= 0 && end - (nameAt + tag.length) <= MAX_ATTRIBUTES_LENGTH && !body.slice(nameAt + tag.length, end).includes("\n")) {
|
|
2019
|
+
if (EXAMPLE_LEAD.test(previousLine.trimEnd())) continue;
|
|
2020
|
+
const close = `</${tag}>`;
|
|
2021
|
+
const rest = body.slice(end + 1);
|
|
2022
|
+
if (rest.includes(close)) return { kind: "tag", name: tag, close, from: indent + end + 1 };
|
|
2023
|
+
if (!complete) {
|
|
2024
|
+
waiting = true;
|
|
2025
|
+
continue;
|
|
2026
|
+
}
|
|
2027
|
+
if (rest.trim() === "" || /^\s*[[{"]/.test(rest)) return { kind: "tag", name: tag, close, from: indent + end + 1 };
|
|
2028
|
+
continue;
|
|
2029
|
+
}
|
|
2030
|
+
if (end < 0 && !complete && body.length - (nameAt + tag.length) <= MAX_ATTRIBUTES_LENGTH) waiting = true;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
for (const format of lines) {
|
|
2034
|
+
if (body.length < format.line.length) {
|
|
2035
|
+
waiting ||= format.line.startsWith(body);
|
|
2036
|
+
continue;
|
|
2037
|
+
}
|
|
2038
|
+
if (!body.startsWith(format.line)) continue;
|
|
2039
|
+
if (!complete) {
|
|
2040
|
+
waiting = true;
|
|
2041
|
+
continue;
|
|
2042
|
+
}
|
|
2043
|
+
if (!format.match || format.match.test(body.trimEnd())) return { kind: "line", format };
|
|
2044
|
+
}
|
|
2045
|
+
return waiting && !complete ? "wait" : "text";
|
|
2046
|
+
}
|
|
2047
|
+
function openBlock(opener, line, newline) {
|
|
2048
|
+
const whole = line + (newline ? "\n" : "");
|
|
2049
|
+
if (opener.kind === "tag") {
|
|
2050
|
+
block = { kind: "tag", name: opener.name, close: opener.close, text: whole, searchFrom: opener.from };
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
const { format } = opener;
|
|
2054
|
+
if (!format.until) {
|
|
2055
|
+
out.push({ kind: "block", name: format.name, text: whole });
|
|
2056
|
+
skipSpace = true;
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
block = format.until === "paragraph" ? { kind: "paragraph", name: format.name, text: whole } : { kind: "until", name: format.name, until: format.until, text: whole };
|
|
2060
|
+
}
|
|
2061
|
+
function feedBlock(current, delta) {
|
|
2062
|
+
current.text += delta;
|
|
2063
|
+
if (current.kind === "tag") {
|
|
2064
|
+
const at = current.text.indexOf(current.close, current.searchFrom);
|
|
2065
|
+
if (at < 0) {
|
|
2066
|
+
current.searchFrom = Math.max(current.searchFrom, current.text.length - current.close.length + 1);
|
|
2067
|
+
return null;
|
|
2068
|
+
}
|
|
2069
|
+
const end = at + current.close.length;
|
|
2070
|
+
return finishBlock(current, end);
|
|
2071
|
+
}
|
|
2072
|
+
let lineStart = current.text.indexOf("\n") + 1;
|
|
2073
|
+
while (lineStart > 0) {
|
|
2074
|
+
const lineEnd = current.text.indexOf("\n", lineStart);
|
|
2075
|
+
if (lineEnd < 0) return null;
|
|
2076
|
+
const line = current.text.slice(lineStart, lineEnd);
|
|
2077
|
+
if (current.kind === "paragraph" && line.trim() === "") return finishBlock(current, lineStart);
|
|
2078
|
+
if (current.kind === "until" && current.until.test(line.trim())) return finishBlock(current, lineEnd + 1);
|
|
2079
|
+
lineStart = lineEnd + 1;
|
|
2080
|
+
}
|
|
2081
|
+
return null;
|
|
2082
|
+
}
|
|
2083
|
+
function finishBlock(current, end) {
|
|
2084
|
+
out.push({ kind: "block", name: current.name, text: current.text.slice(0, end) });
|
|
2085
|
+
block = null;
|
|
2086
|
+
skipSpace = true;
|
|
2087
|
+
previousLine = "";
|
|
2088
|
+
return current.text.slice(end);
|
|
2089
|
+
}
|
|
2090
|
+
function feed(delta) {
|
|
2091
|
+
let rest = delta;
|
|
2092
|
+
while (rest) {
|
|
2093
|
+
if (block) {
|
|
2094
|
+
const left = feedBlock(block, rest);
|
|
2095
|
+
if (left === null) return;
|
|
2096
|
+
rest = left;
|
|
2097
|
+
continue;
|
|
2098
|
+
}
|
|
2099
|
+
if (skipSpace) {
|
|
2100
|
+
const space = /^\s*/.exec(rest)[0];
|
|
2101
|
+
if (space) emitGap(space);
|
|
2102
|
+
rest = rest.slice(space.length);
|
|
2103
|
+
if (!rest) return;
|
|
2104
|
+
skipSpace = false;
|
|
2105
|
+
pending = "";
|
|
2106
|
+
ordinaryLine = null;
|
|
2107
|
+
continue;
|
|
2108
|
+
}
|
|
2109
|
+
if (ordinaryLine !== null) {
|
|
2110
|
+
const newline2 = rest.indexOf("\n");
|
|
2111
|
+
if (newline2 < 0) {
|
|
2112
|
+
ordinaryLine += rest;
|
|
2113
|
+
emitText(rest);
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
closeOrdinaryLine(ordinaryLine + rest.slice(0, newline2));
|
|
2117
|
+
emitText(rest.slice(0, newline2 + 1));
|
|
2118
|
+
ordinaryLine = null;
|
|
2119
|
+
rest = rest.slice(newline2 + 1);
|
|
2120
|
+
continue;
|
|
2121
|
+
}
|
|
2122
|
+
const newline = rest.indexOf("\n");
|
|
2123
|
+
const line = pending + (newline < 0 ? rest : rest.slice(0, newline));
|
|
2124
|
+
const verdict = classify(line, newline >= 0);
|
|
2125
|
+
if (verdict === "wait") {
|
|
2126
|
+
pending = line;
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
pending = "";
|
|
2130
|
+
const after = newline < 0 ? "" : rest.slice(newline + 1);
|
|
2131
|
+
if (verdict === "text") {
|
|
2132
|
+
if (newline < 0) {
|
|
2133
|
+
ordinaryLine = line;
|
|
2134
|
+
emitText(line);
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
closeOrdinaryLine(line);
|
|
2138
|
+
emitText(`${line}
|
|
2139
|
+
`);
|
|
2140
|
+
rest = after;
|
|
2141
|
+
continue;
|
|
2142
|
+
}
|
|
2143
|
+
openBlock(verdict, line, newline >= 0);
|
|
2144
|
+
rest = after;
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
const take = () => {
|
|
2148
|
+
const pieces = out;
|
|
2149
|
+
out = [];
|
|
2150
|
+
return pieces;
|
|
2151
|
+
};
|
|
2152
|
+
return {
|
|
2153
|
+
/** Следующий кусок текста → готовые куски. Начало возможного блока придерживается до следующего. */
|
|
2154
|
+
push(delta) {
|
|
2155
|
+
feed(delta);
|
|
2156
|
+
return take();
|
|
2157
|
+
},
|
|
2158
|
+
/**
|
|
2159
|
+
* Текст кончился: придержанное решается на неполной строке. Незакрытый тег — блок до первой пустой строки, остальное
|
|
2160
|
+
* разбирается дальше; строчный блок — до конца текста.
|
|
2161
|
+
*/
|
|
2162
|
+
end() {
|
|
2163
|
+
for (; ; ) {
|
|
2164
|
+
const current = block;
|
|
2165
|
+
if (current) {
|
|
2166
|
+
block = null;
|
|
2167
|
+
const blank = current.kind === "tag" ? paragraphEnd(current.text) : -1;
|
|
2168
|
+
if (blank < 0) {
|
|
2169
|
+
out.push({ kind: "block", name: current.name, text: current.text });
|
|
2170
|
+
continue;
|
|
2171
|
+
}
|
|
2172
|
+
finishBlock(current, blank);
|
|
2173
|
+
feed(current.text.slice(blank));
|
|
2174
|
+
continue;
|
|
2175
|
+
}
|
|
2176
|
+
if (!pending) break;
|
|
2177
|
+
const line = pending;
|
|
2178
|
+
pending = "";
|
|
2179
|
+
const verdict = classify(line, true);
|
|
2180
|
+
if (verdict === "text" || verdict === "wait") emitText(line);
|
|
2181
|
+
else openBlock(verdict, line, false);
|
|
2182
|
+
}
|
|
2183
|
+
return take();
|
|
2184
|
+
}
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
function cutForgedBlocks(text, formats, options2 = {}) {
|
|
2188
|
+
if (!compile(formats).hint.test(text)) return { text, blocks: [] };
|
|
2189
|
+
const scanner = createForgedBlockScanner(formats, options2);
|
|
2190
|
+
const pieces = [...scanner.push(text), ...scanner.end()];
|
|
2191
|
+
return {
|
|
2192
|
+
text: pieces.map((piece) => piece.kind === "text" ? piece.text : "").join(""),
|
|
2193
|
+
blocks: pieces.flatMap((piece) => piece.kind === "block" ? [piece.name] : [])
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
var hasWords = (text) => /[\p{L}\p{N}]/u.test(text);
|
|
2197
|
+
function cutForgedBlocksInParts(parts, formats, options2 = {}) {
|
|
2198
|
+
const blocks = [];
|
|
2199
|
+
const cleaned = parts.flatMap((part) => {
|
|
2200
|
+
if (part.type !== "text") return [part];
|
|
2201
|
+
const cut = cutForgedBlocks(part.text, formats, options2);
|
|
2202
|
+
if (cut.blocks.length === 0) return [part];
|
|
2203
|
+
blocks.push(...cut.blocks);
|
|
2204
|
+
return cut.text.trim() === "" ? [] : [{ ...part, text: cut.text }];
|
|
2205
|
+
});
|
|
2206
|
+
if (blocks.length === 0) return null;
|
|
2207
|
+
if (!cleaned.some((part) => part.type === "text" && hasWords(part.text))) return null;
|
|
2208
|
+
return { parts: cleaned, blocks };
|
|
2209
|
+
}
|
|
2210
|
+
function withoutForgedBlocks(history, formats) {
|
|
2211
|
+
let changed = false;
|
|
2212
|
+
let question2 = "";
|
|
2213
|
+
const cleaned = history.map((message) => {
|
|
2214
|
+
if (message.role === "user") question2 = textOfMessage(message);
|
|
2215
|
+
if (message.role !== "assistant") return message;
|
|
2216
|
+
const cut = cutForgedBlocksInParts(message.parts, formats, { explaining: explainsServiceFormats(question2, formats) });
|
|
2217
|
+
if (!cut) return message;
|
|
2218
|
+
changed = true;
|
|
2219
|
+
return { ...message, parts: cut.parts };
|
|
2220
|
+
});
|
|
2221
|
+
return changed ? cleaned : history;
|
|
2222
|
+
}
|
|
2223
|
+
function createForgedBlockGuard(formats) {
|
|
2224
|
+
let worded = false;
|
|
2225
|
+
let explaining = false;
|
|
2226
|
+
const cut = [];
|
|
2227
|
+
const kept = [];
|
|
2228
|
+
const transformFor = () => {
|
|
2229
|
+
const scanners = /* @__PURE__ */ new Map();
|
|
2230
|
+
let held = null;
|
|
2231
|
+
const starts = /* @__PURE__ */ new Map();
|
|
2232
|
+
const started = /* @__PURE__ */ new Set();
|
|
2233
|
+
const enqueue = (part, controller) => {
|
|
2234
|
+
if (part.type === "text-start" && scanners.has(part.id)) {
|
|
2235
|
+
starts.set(part.id, part);
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
if (part.type === "text-delta" && !started.has(part.id) && starts.has(part.id)) {
|
|
2239
|
+
controller.enqueue(starts.get(part.id));
|
|
2240
|
+
started.add(part.id);
|
|
2241
|
+
}
|
|
2242
|
+
if (part.type === "text-end" && starts.has(part.id) && !started.has(part.id)) return;
|
|
2243
|
+
controller.enqueue(part);
|
|
2244
|
+
};
|
|
2245
|
+
const isBlock = (item) => "name" in item && "text" in item && !("type" in item);
|
|
2246
|
+
const settle = (keep, controller) => {
|
|
2247
|
+
const items = held ?? [];
|
|
2248
|
+
held = null;
|
|
2249
|
+
for (const item of items) {
|
|
2250
|
+
if (!isBlock(item)) enqueue(item, controller);
|
|
2251
|
+
else {
|
|
2252
|
+
if (keep) enqueue({ type: "text-delta", id: item.id, delta: item.text }, controller);
|
|
2253
|
+
if (item.name) (keep ? kept : cut).push(item.name);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
};
|
|
2257
|
+
const send = (part, controller) => {
|
|
2258
|
+
if (held) held.push(part);
|
|
2259
|
+
else enqueue(part, controller);
|
|
2260
|
+
};
|
|
2261
|
+
const deltaOf = (id, pieces, controller) => {
|
|
2262
|
+
let delta = "";
|
|
2263
|
+
for (const piece of pieces) {
|
|
2264
|
+
if (piece.kind === "text" && hasWords(piece.text)) {
|
|
2265
|
+
worded = true;
|
|
2266
|
+
if (held) settle(false, controller);
|
|
2267
|
+
}
|
|
2268
|
+
if (piece.kind === "text" && !held) delta += piece.text;
|
|
2269
|
+
else if (worded) {
|
|
2270
|
+
if (piece.kind === "block") cut.push(piece.name);
|
|
2271
|
+
} else {
|
|
2272
|
+
(held ??= []).push({ id, name: piece.kind === "block" ? piece.name : "", text: piece.text });
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
return delta;
|
|
2276
|
+
};
|
|
2277
|
+
const transform = (part, controller) => {
|
|
2278
|
+
switch (part.type) {
|
|
2279
|
+
case "text-start":
|
|
2280
|
+
scanners.set(part.id, createForgedBlockScanner(formats, { explaining }));
|
|
2281
|
+
break;
|
|
2282
|
+
case "text-delta": {
|
|
2283
|
+
const scanner = scanners.get(part.id);
|
|
2284
|
+
if (!scanner) break;
|
|
2285
|
+
const delta = deltaOf(part.id, scanner.push(part.delta), controller);
|
|
2286
|
+
if (delta) send({ ...part, delta }, controller);
|
|
2287
|
+
return;
|
|
2288
|
+
}
|
|
2289
|
+
case "text-end": {
|
|
2290
|
+
const scanner = scanners.get(part.id);
|
|
2291
|
+
if (!scanner) break;
|
|
2292
|
+
scanners.delete(part.id);
|
|
2293
|
+
const delta = deltaOf(part.id, scanner.end(), controller);
|
|
2294
|
+
if (delta) send({ type: "text-delta", id: part.id, delta }, controller);
|
|
2295
|
+
break;
|
|
2296
|
+
}
|
|
2297
|
+
case "tool-input-start":
|
|
2298
|
+
case "tool-call":
|
|
2299
|
+
if (held) settle(false, controller);
|
|
2300
|
+
break;
|
|
2301
|
+
case "finish":
|
|
2302
|
+
if (held) settle(part.finishReason.unified !== "tool-calls", controller);
|
|
2303
|
+
break;
|
|
2304
|
+
}
|
|
2305
|
+
send(part, controller);
|
|
2306
|
+
};
|
|
2307
|
+
const flush = (controller) => {
|
|
2308
|
+
if (held) settle(true, controller);
|
|
2309
|
+
};
|
|
2310
|
+
return new TransformStream({ transform, flush });
|
|
2311
|
+
};
|
|
2312
|
+
return {
|
|
2313
|
+
/**
|
|
2314
|
+
* Новая попытка с последним вопросом человека: шаги прежней стёрты — слов у ответа снова нет. Вырезанное раньше
|
|
2315
|
+
* остаётся в счёте для журнала.
|
|
2316
|
+
*/
|
|
2317
|
+
reset(question2 = "") {
|
|
2318
|
+
worded = false;
|
|
2319
|
+
explaining = explainsServiceFormats(question2, formats);
|
|
2320
|
+
},
|
|
2321
|
+
/** Законченный текст мимо потока (переписка проверкой) — по тем же правилам, что поток этой попытки. */
|
|
2322
|
+
cutText: (text) => cutForgedBlocks(text, formats, { explaining }),
|
|
2323
|
+
/** Модель попытки под присмотром: каждый запрос — новый шаг. */
|
|
2324
|
+
wrap(model) {
|
|
2325
|
+
if (typeof model === "string" || formats.length === 0) return model;
|
|
2326
|
+
return wrapLanguageModel2({
|
|
2327
|
+
model,
|
|
2328
|
+
middleware: {
|
|
2329
|
+
specificationVersion: "v4",
|
|
2330
|
+
wrapStream: async ({ doStream }) => {
|
|
2331
|
+
const result = await doStream();
|
|
2332
|
+
return { ...result, stream: result.stream.pipeThrough(transformFor()) };
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
});
|
|
2336
|
+
},
|
|
2337
|
+
/** Вырезанные блоки хода по порядку (имена) — для журнала. */
|
|
2338
|
+
cutBlocks: () => cut,
|
|
2339
|
+
/** Блоки, оставленные ради непустого ответа. */
|
|
2340
|
+
keptBlocks: () => kept
|
|
2341
|
+
};
|
|
2342
|
+
}
|
|
2343
|
+
|
|
1856
2344
|
// src/storage/chat-storage.ts
|
|
1857
2345
|
var RELEASED_ANSWER_ALIVE_AT = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
1858
2346
|
function aliveAtByLocalClock(mark) {
|
|
@@ -2484,10 +2972,9 @@ var NOT_RETRYABLE_SIGNS = signs([
|
|
|
2484
2972
|
"available balance",
|
|
2485
2973
|
"insufficient_quota",
|
|
2486
2974
|
"out of budget",
|
|
2487
|
-
"quota exceeded",
|
|
2488
|
-
"billing",
|
|
2489
2975
|
"subscription_sharing_usage_limit_exceeded"
|
|
2490
2976
|
]);
|
|
2977
|
+
var QUOTA_WORDS = signs(["quota exceeded", "billing"]);
|
|
2491
2978
|
var RETRYABLE_SIGNS = signs([
|
|
2492
2979
|
"overloaded",
|
|
2493
2980
|
"currently experiencing high demand",
|
|
@@ -2534,8 +3021,8 @@ function isContextOverflow(text, statusCode) {
|
|
|
2534
3021
|
if (NOT_OVERFLOW_SIGNS.some((sign) => sign.test(text))) return false;
|
|
2535
3022
|
return OVERFLOW_SIGNS.some((sign) => sign.test(text));
|
|
2536
3023
|
}
|
|
2537
|
-
function isNotRetryable(text) {
|
|
2538
|
-
return NOT_RETRYABLE_SIGNS.test(text);
|
|
3024
|
+
function isNotRetryable(text, statusCode) {
|
|
3025
|
+
return NOT_RETRYABLE_SIGNS.test(text) || statusCode !== 429 && QUOTA_WORDS.test(text);
|
|
2539
3026
|
}
|
|
2540
3027
|
function looksTransient(text) {
|
|
2541
3028
|
return RETRYABLE_SIGNS.test(text);
|
|
@@ -2573,7 +3060,7 @@ function isTransient(error) {
|
|
|
2573
3060
|
if (verdict === "false") return false;
|
|
2574
3061
|
const status = errorStatusCode(error);
|
|
2575
3062
|
const text = errorText(error);
|
|
2576
|
-
if (isNotRetryable(text) || isContextOverflow(text, status)) return false;
|
|
3063
|
+
if (isNotRetryable(text, status) || isContextOverflow(text, status)) return false;
|
|
2577
3064
|
if (status !== void 0) return RETRYABLE_STATUS_CODES.has(status) || status >= 500;
|
|
2578
3065
|
if (APICallError2.isInstance(error) && error.isRetryable) return true;
|
|
2579
3066
|
return looksTransient(text);
|
|
@@ -2747,6 +3234,9 @@ function sleep(ms, signal) {
|
|
|
2747
3234
|
}
|
|
2748
3235
|
|
|
2749
3236
|
// src/provider-errors/classify-provider-error.ts
|
|
3237
|
+
var KEY_SIGNS = /api[ _-]?key|authenticat|unauthori[sz]ed|credential|access[ _-]?token|key (?:is )?(?:invalid|revoked|expired|disabled)/i;
|
|
3238
|
+
var FUNDS_SIGNS = /key limit exceeded|insufficient (?:credits|funds|balance)/i;
|
|
3239
|
+
var MODERATION_SIGNS = /flagged|moderation|content[ _-]?policy|safety/i;
|
|
2750
3240
|
var RATE_LIMIT_SIGNS = /rate.?limit|too many requests|throttl|please wait|ResourceExhausted/i;
|
|
2751
3241
|
function classifyProviderError(error, provider) {
|
|
2752
3242
|
if (error instanceof MissingProviderKeyError) return "invalid_key";
|
|
@@ -2756,8 +3246,12 @@ function classifyProviderError(error, provider) {
|
|
|
2756
3246
|
for (const hint of provider?.errorHints ?? []) {
|
|
2757
3247
|
if (hint.match.test(text)) return hint.kind;
|
|
2758
3248
|
}
|
|
2759
|
-
if (status === 401
|
|
2760
|
-
if (status ===
|
|
3249
|
+
if (status === 401) return "invalid_key";
|
|
3250
|
+
if (status === 403) {
|
|
3251
|
+
if (KEY_SIGNS.test(text)) return "invalid_key";
|
|
3252
|
+
return FUNDS_SIGNS.test(text) || isNotRetryable(text) ? "insufficient_funds" : "other";
|
|
3253
|
+
}
|
|
3254
|
+
if (status === 402 || isNotRetryable(text, status)) return "insufficient_funds";
|
|
2761
3255
|
if (isContextOverflow(text, status)) return "context_overflow";
|
|
2762
3256
|
if (status === 429) return "rate_limited";
|
|
2763
3257
|
if (isProviderSilence(cause)) return "overloaded";
|
|
@@ -2765,6 +3259,10 @@ function classifyProviderError(error, provider) {
|
|
|
2765
3259
|
if (RATE_LIMIT_SIGNS.test(text)) return "rate_limited";
|
|
2766
3260
|
return looksTransient(text) ? "overloaded" : "other";
|
|
2767
3261
|
}
|
|
3262
|
+
function isAccessRefusal(error) {
|
|
3263
|
+
const cause = underlyingError(error);
|
|
3264
|
+
return errorStatusCode(cause) === 403 && !MODERATION_SIGNS.test(errorText(cause));
|
|
3265
|
+
}
|
|
2768
3266
|
|
|
2769
3267
|
// src/provider-errors/provider-failure-log.ts
|
|
2770
3268
|
import { APICallError as APICallError3 } from "ai";
|
|
@@ -2790,6 +3288,49 @@ function oneLine(text) {
|
|
|
2790
3288
|
return text.replace(/\s*[\r\n]+\s*/g, " ");
|
|
2791
3289
|
}
|
|
2792
3290
|
|
|
3291
|
+
// src/provider-errors/provider-outages.ts
|
|
3292
|
+
function isProviderOutage(kind) {
|
|
3293
|
+
return kind === "insufficient_funds" || kind === "invalid_key";
|
|
3294
|
+
}
|
|
3295
|
+
var PROVIDER_OUTAGE_PAUSE_MS = 5 * 6e4;
|
|
3296
|
+
var PROVIDER_OUTAGE_ALERT_EVERY_MS = 15 * 6e4;
|
|
3297
|
+
var PROVIDER_OUTAGE_PROBE_MS = 6e4;
|
|
3298
|
+
var SAID = {
|
|
3299
|
+
insufficient_funds: "\u043A\u043E\u043D\u0447\u0438\u043B\u0438\u0441\u044C \u0434\u0435\u043D\u044C\u0433\u0438",
|
|
3300
|
+
invalid_key: "\u043A\u043B\u044E\u0447 \u043D\u0435 \u043F\u0440\u0438\u043D\u044F\u0442"
|
|
3301
|
+
};
|
|
3302
|
+
function createProviderOutages(options2) {
|
|
3303
|
+
const outages = /* @__PURE__ */ new Map();
|
|
3304
|
+
const alertedAt = /* @__PURE__ */ new Map();
|
|
3305
|
+
const pausedKind = (providerId) => {
|
|
3306
|
+
const outage = outages.get(providerId);
|
|
3307
|
+
if (!outage) return void 0;
|
|
3308
|
+
const time = options2.now();
|
|
3309
|
+
return time < outage.pausedUntil || time < (outage.probeUntil ?? 0) ? outage.kind : void 0;
|
|
3310
|
+
};
|
|
3311
|
+
return {
|
|
3312
|
+
report({ providerId, kind, fallbackProviderId, fallbacksPaused }) {
|
|
3313
|
+
const time = options2.now();
|
|
3314
|
+
outages.set(providerId, { kind, pausedUntil: time + PROVIDER_OUTAGE_PAUSE_MS });
|
|
3315
|
+
const last = alertedAt.get(providerId);
|
|
3316
|
+
if (last !== void 0 && time - last < PROVIDER_OUTAGE_ALERT_EVERY_MS) return;
|
|
3317
|
+
alertedAt.set(providerId, time);
|
|
3318
|
+
const route = fallbackProviderId !== void 0 ? `\u0445\u043E\u0434\u044B \u0438\u0434\u0443\u0442 \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0439 ${fallbackProviderId}` : `${fallbacksPaused ? "\u0437\u0430\u043F\u0430\u0441\u043D\u044B\u0435 \u0442\u043E\u0436\u0435 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435" : "\u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0433\u043E \u043D\u0435\u0442"}, \u0445\u043E\u0434\u044B \u043F\u0430\u0434\u0430\u044E\u0442 \u0441 \u043E\u0448\u0438\u0431\u043A\u043E\u0439`;
|
|
3319
|
+
options2.logError(`[\u0412\u041D\u0418\u041C\u0410\u041D\u0418\u0415 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440] ${providerId}: ${SAID[kind]} \u2014 ${route}`);
|
|
3320
|
+
},
|
|
3321
|
+
pausedKind,
|
|
3322
|
+
admit(providerId) {
|
|
3323
|
+
const kind = pausedKind(providerId);
|
|
3324
|
+
const outage = outages.get(providerId);
|
|
3325
|
+
if (kind === void 0 && outage) outage.probeUntil = options2.now() + PROVIDER_OUTAGE_PROBE_MS;
|
|
3326
|
+
return kind;
|
|
3327
|
+
},
|
|
3328
|
+
recovered(providerId) {
|
|
3329
|
+
outages.delete(providerId);
|
|
3330
|
+
}
|
|
3331
|
+
};
|
|
3332
|
+
}
|
|
3333
|
+
|
|
2793
3334
|
// src/tool-approval/tool-approval-policy.ts
|
|
2794
3335
|
var needsApprovalOf = (tool4) => tool4.needsApproval;
|
|
2795
3336
|
function prepareToolApproval(tools, alwaysAllowed) {
|
|
@@ -2865,7 +3406,7 @@ function usageMinusJournaled(usage, journaled) {
|
|
|
2865
3406
|
}
|
|
2866
3407
|
|
|
2867
3408
|
// src/agent-run/build-model-request.ts
|
|
2868
|
-
import { convertToModelMessages, wrapLanguageModel as
|
|
3409
|
+
import { convertToModelMessages, wrapLanguageModel as wrapLanguageModel5 } from "ai";
|
|
2869
3410
|
|
|
2870
3411
|
// src/conversation/hanging-tool-calls.ts
|
|
2871
3412
|
var PRELIMINARY_OUTPUT_MAX_LENGTH = 2e3;
|
|
@@ -3085,7 +3626,7 @@ ${chunk.delta}` } : chunk];
|
|
|
3085
3626
|
}
|
|
3086
3627
|
|
|
3087
3628
|
// src/agent-run/observe-model-calls.ts
|
|
3088
|
-
import { wrapLanguageModel as
|
|
3629
|
+
import { wrapLanguageModel as wrapLanguageModel3 } from "ai";
|
|
3089
3630
|
function observeModelCalls(model, context) {
|
|
3090
3631
|
if (typeof model === "string") return model;
|
|
3091
3632
|
const now = context.now ?? Date.now;
|
|
@@ -3095,7 +3636,7 @@ function observeModelCalls(model, context) {
|
|
|
3095
3636
|
attempts.set(params.prompt, attempt);
|
|
3096
3637
|
return attempt;
|
|
3097
3638
|
};
|
|
3098
|
-
return
|
|
3639
|
+
return wrapLanguageModel3({
|
|
3099
3640
|
model,
|
|
3100
3641
|
middleware: {
|
|
3101
3642
|
specificationVersion: "v4",
|
|
@@ -3526,21 +4067,6 @@ function estimateMessagesTokens(messages) {
|
|
|
3526
4067
|
}, 0);
|
|
3527
4068
|
}
|
|
3528
4069
|
|
|
3529
|
-
// src/agent-run/length-cut.ts
|
|
3530
|
-
var LENGTH_CUT_PART_TYPE = "data-length-cut";
|
|
3531
|
-
var LENGTH_CUT_NOTE = "[\u041E\u0442\u0432\u0435\u0442 \u043E\u0431\u043E\u0440\u0432\u0430\u043D: \u043C\u043E\u0434\u0435\u043B\u044C \u0443\u043F\u0451\u0440\u043B\u0430\u0441\u044C \u0432 \u043F\u0440\u0435\u0434\u0435\u043B \u0434\u043B\u0438\u043D\u044B \u043E\u0442\u0432\u0435\u0442\u0430]";
|
|
3532
|
-
function lengthCutForModel(part) {
|
|
3533
|
-
return part.type === LENGTH_CUT_PART_TYPE ? { type: "text", text: LENGTH_CUT_NOTE } : void 0;
|
|
3534
|
-
}
|
|
3535
|
-
|
|
3536
|
-
// src/agent-run/unanswered-messages.ts
|
|
3537
|
-
var UNANSWERED_NOTE = "[\u041E\u0442\u0432\u0435\u0442\u0430 \u043D\u0430 \u044D\u0442\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u043D\u0435\u0442: \u0445\u043E\u0434 \u043E\u0431\u043E\u0440\u0432\u0430\u043B\u0441\u044F \u0434\u043E \u043F\u0435\u0440\u0432\u044B\u0445 \u0441\u043B\u043E\u0432]";
|
|
3538
|
-
function withUnansweredMarked(messages) {
|
|
3539
|
-
return messages.flatMap(
|
|
3540
|
-
(message, index) => message.role === "user" && messages[index - 1]?.role === "user" ? [{ role: "assistant", content: [{ type: "text", text: UNANSWERED_NOTE }] }, message] : [message]
|
|
3541
|
-
);
|
|
3542
|
-
}
|
|
3543
|
-
|
|
3544
4070
|
// src/agent-run/staff-replies.ts
|
|
3545
4071
|
function isStaffReply(message) {
|
|
3546
4072
|
if (message.role !== "user") return false;
|
|
@@ -3698,7 +4224,7 @@ function withoutUndelivered(history) {
|
|
|
3698
4224
|
}
|
|
3699
4225
|
|
|
3700
4226
|
// src/agent-run/garbled-tail.ts
|
|
3701
|
-
import { wrapLanguageModel as
|
|
4227
|
+
import { wrapLanguageModel as wrapLanguageModel4 } from "ai";
|
|
3702
4228
|
var SIGN_ORDER = ["think", "role", "restart", "tool"];
|
|
3703
4229
|
var TOKEN_SOURCE = String.raw`<\/?think>|<\|[^|\n<>]{1,40}\|>|<|[^|\n<>]{1,40}|>`;
|
|
3704
4230
|
var TEMPLATE_TOKEN = new RegExp(TOKEN_SOURCE, "g");
|
|
@@ -3870,7 +4396,7 @@ function createGarbledTailGuard() {
|
|
|
3870
4396
|
/** Модель попытки под присмотром: каждый запрос — новый шаг. */
|
|
3871
4397
|
wrap(model) {
|
|
3872
4398
|
if (typeof model === "string") return model;
|
|
3873
|
-
return
|
|
4399
|
+
return wrapLanguageModel4({
|
|
3874
4400
|
model,
|
|
3875
4401
|
middleware: {
|
|
3876
4402
|
specificationVersion: "v4",
|
|
@@ -3975,16 +4501,16 @@ function visibleBefore(earlier, visible, offset) {
|
|
|
3975
4501
|
function withoutCodeAndQuotes(text) {
|
|
3976
4502
|
let fence = null;
|
|
3977
4503
|
return text.split("\n").map((line) => {
|
|
3978
|
-
const opener = /^ {0,3}(`{3,}|~{3,})/.exec(line)?.[1];
|
|
3979
4504
|
if (fence !== null) {
|
|
3980
|
-
if (
|
|
4505
|
+
if (closesCodeFence(line, fence)) fence = null;
|
|
3981
4506
|
return " ".repeat(line.length);
|
|
3982
4507
|
}
|
|
4508
|
+
const opener = codeFenceOf(line);
|
|
3983
4509
|
if (opener) {
|
|
3984
4510
|
fence = opener;
|
|
3985
4511
|
return " ".repeat(line.length);
|
|
3986
4512
|
}
|
|
3987
|
-
if (
|
|
4513
|
+
if (isIndentedCodeOrQuote(line)) return " ".repeat(line.length);
|
|
3988
4514
|
return line.replace(/(`+)[^`\n]*?\1/g, (code) => " ".repeat(code.length));
|
|
3989
4515
|
}).join("\n");
|
|
3990
4516
|
}
|
|
@@ -4012,7 +4538,8 @@ async function buildModelRequest(input) {
|
|
|
4012
4538
|
...input.repeatSilence && { repeatSilence: input.repeatSilence }
|
|
4013
4539
|
});
|
|
4014
4540
|
const tools = input.tools ? sortToolsByName(input.tools) : void 0;
|
|
4015
|
-
const
|
|
4541
|
+
const cleaned = sendableHistory(input.serviceBlocks ? withoutForgedBlocks(input.history, input.serviceBlocks) : input.history);
|
|
4542
|
+
const sendable = input.switchableTools ? withSwitchedOffCallsAsText(cleaned, input.switchableTools, tools) : cleaned;
|
|
4016
4543
|
const noted = withStaffRepliesJoined(withChannelNotes(withModeNotes(withBurstsJoined(withCorrectionsJoined(sendable)), input.modeNotes)));
|
|
4017
4544
|
const held = input.frozenContext ? holdNoteAfterApprovals(withFrozenContext(noted, input.frozenContext)) : { messages: noted };
|
|
4018
4545
|
const history = selectReasoningForModel(held.messages, {
|
|
@@ -4097,7 +4624,7 @@ function withoutLoneSurrogatesDeep(value) {
|
|
|
4097
4624
|
}
|
|
4098
4625
|
function withRequestWatch(model, provider, watch) {
|
|
4099
4626
|
if (!watch || typeof model === "string") return model;
|
|
4100
|
-
return
|
|
4627
|
+
return wrapLanguageModel5({
|
|
4101
4628
|
model,
|
|
4102
4629
|
middleware: {
|
|
4103
4630
|
specificationVersion: "v4",
|
|
@@ -4117,7 +4644,7 @@ function withRequestWatch(model, provider, watch) {
|
|
|
4117
4644
|
});
|
|
4118
4645
|
}
|
|
4119
4646
|
function withTransientRetries(model, context) {
|
|
4120
|
-
return typeof model === "string" ? model :
|
|
4647
|
+
return typeof model === "string" ? model : wrapLanguageModel5({ model, middleware: createTransientRetryMiddleware(context) });
|
|
4121
4648
|
}
|
|
4122
4649
|
function withLastMessageMark(messages, options2) {
|
|
4123
4650
|
if (!options2 || messages.length === 0) return messages;
|
|
@@ -4348,8 +4875,14 @@ function decideNextAttempt(result, state) {
|
|
|
4348
4875
|
if (state.overflowCompaction?.answerSteps) return { action: "compact-and-continue", scope: "answer-steps" };
|
|
4349
4876
|
return { action: "fail" };
|
|
4350
4877
|
}
|
|
4351
|
-
if (
|
|
4878
|
+
if (isProviderOutage(result.kind)) {
|
|
4879
|
+
const next2 = state.fallbackProviderIds[0];
|
|
4880
|
+
return next2 === void 0 ? { action: "fail" } : { action: "fallback", providerId: next2 };
|
|
4881
|
+
}
|
|
4882
|
+
const accessRefused = result.kind === "other" && isAccessRefusal(result.error);
|
|
4883
|
+
if (!TRANSIENT_KINDS.has(result.kind) && !accessRefused) return { action: "fail" };
|
|
4352
4884
|
if (result.contentSeen) {
|
|
4885
|
+
if (accessRefused) return { action: "fail" };
|
|
4353
4886
|
const auto = state.autoContinue;
|
|
4354
4887
|
if (!auto || auto.approvalRequested || auto.done >= AUTO_CONTINUES_PER_TURN) return { action: "fail" };
|
|
4355
4888
|
return { action: "auto-continue", pauseMs: autoContinuePauseMs({ index: auto.done, error: result.error, random: auto.random ?? Math.random }) };
|
|
@@ -4358,6 +4891,10 @@ function decideNextAttempt(result, state) {
|
|
|
4358
4891
|
if (state.fallbackUsed || next === void 0) return { action: "fail" };
|
|
4359
4892
|
return { action: "fallback", providerId: next };
|
|
4360
4893
|
}
|
|
4894
|
+
function errorKindForPerson(lastKind, firstOutage) {
|
|
4895
|
+
if (firstOutage === void 0 || TRANSIENT_KINDS.has(lastKind) || lastKind === "context_overflow") return lastKind;
|
|
4896
|
+
return firstOutage;
|
|
4897
|
+
}
|
|
4361
4898
|
function checkProviderFallback(setting, catalog) {
|
|
4362
4899
|
if (setting === void 0 || setting === false) return;
|
|
4363
4900
|
if (typeof setting !== "object" || setting === null || Array.isArray(setting)) {
|
|
@@ -5907,6 +6444,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
5907
6444
|
);
|
|
5908
6445
|
const turnUsage = createTurnUsage();
|
|
5909
6446
|
const garbledTail = createGarbledTailGuard();
|
|
6447
|
+
const forgedBlocks = createForgedBlockGuard(turnInput.serviceBlocks ?? CORE_SERVICE_BLOCKS);
|
|
5910
6448
|
const turnBudget = createTurnBudget(input.turnRules, { catalog: input.catalog, modelId: input.modelId, turnUsage });
|
|
5911
6449
|
const messageUsage = () => {
|
|
5912
6450
|
const spent = turnUsage.total();
|
|
@@ -5995,13 +6533,21 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
5995
6533
|
const seen = latestFrozenContextOf(resume.message.parts);
|
|
5996
6534
|
if (seen) resumeNote = { ...resumeNote ?? { blocks: seen.blocks, sent: { changed: [], removed: [] }, base: messageId }, notices: turnNotices };
|
|
5997
6535
|
}
|
|
5998
|
-
const
|
|
6536
|
+
const outages = input.providerOutages ?? createProviderOutages({ now: () => (input.now ?? (() => /* @__PURE__ */ new Date()))().getTime(), logError });
|
|
6537
|
+
const allFallbackIds = fallbackProviders({
|
|
5999
6538
|
catalog: input.catalog,
|
|
6000
6539
|
modelId,
|
|
6001
6540
|
chosenProviderId: input.providerId,
|
|
6002
6541
|
env: input.env,
|
|
6003
6542
|
...input.providerFallback !== void 0 && { setting: input.providerFallback }
|
|
6004
6543
|
});
|
|
6544
|
+
const fallbackProviderIds = allFallbackIds.filter((candidate) => outages.pausedKind(candidate) === void 0);
|
|
6545
|
+
let firstOutage;
|
|
6546
|
+
const primaryPaused = outages.admit(input.providerId);
|
|
6547
|
+
if (primaryPaused !== void 0 && fallbackProviderIds[0] !== void 0) {
|
|
6548
|
+
providerId = fallbackProviderIds[0];
|
|
6549
|
+
logError(`[\u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440] \u0447\u0430\u0442=${input.chatId} \u043C\u043E\u0434\u0435\u043B\u044C=${modelId} \u0441=${input.providerId} \u043D\u0430=${providerId} \u043F\u0440\u0438\u0447\u0438\u043D\u0430=${primaryPaused} \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435`);
|
|
6550
|
+
}
|
|
6005
6551
|
emit({ type: "start", messageId, messageMetadata: { modelId, providerId: input.providerId } });
|
|
6006
6552
|
if (input.liveDemos && chatHasExternalContent(await storage.listMessages(input.chatId)) && externalContent.announce()) emitRaw(externalContentChunk());
|
|
6007
6553
|
const silenceSetting = resolveSilenceSwitch(input.providerFallback);
|
|
@@ -6075,6 +6621,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6075
6621
|
turnUsage,
|
|
6076
6622
|
turnBudget,
|
|
6077
6623
|
garbledTail,
|
|
6624
|
+
forgedBlocks,
|
|
6078
6625
|
// Есть куда перейти при молчании — у того же провайдера оно не повторяется.
|
|
6079
6626
|
repeatSilence: () => silenceTarget() === void 0,
|
|
6080
6627
|
...silenceSwitch.scope === "step" && switchedBySilence && {
|
|
@@ -6092,13 +6639,14 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6092
6639
|
} catch (error2) {
|
|
6093
6640
|
if (error2 instanceof MissingProviderKeyError) logError(error2.message);
|
|
6094
6641
|
if (!input.abortSignal.aborted) {
|
|
6095
|
-
errorKind = classify(error2);
|
|
6642
|
+
errorKind = errorKindForPerson(classify(error2), firstOutage);
|
|
6096
6643
|
errors = [{ type: "error", errorText: errorKind }];
|
|
6097
6644
|
}
|
|
6098
6645
|
break;
|
|
6099
6646
|
}
|
|
6100
6647
|
const { result } = attempt;
|
|
6101
6648
|
historyRewritten = false;
|
|
6649
|
+
if (result.outcome === "ok") outages.recovered(providerId);
|
|
6102
6650
|
if (result.outcome !== "failed" || result.contentSeen) answeredProviderId = providerId;
|
|
6103
6651
|
if (result.outcome === "ok" && backToPrimary) {
|
|
6104
6652
|
backToPrimary = false;
|
|
@@ -6133,12 +6681,24 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6133
6681
|
silenceSwitch: { done: silenceSwitches, max: silenceSwitch.maxSwitches, nextProviderId: silenceTarget() }
|
|
6134
6682
|
});
|
|
6135
6683
|
let next = stepsDone < maxSteps ? decideNextAttempt(result, nextState()) : { action: "fail" };
|
|
6684
|
+
const outage = isProviderOutage(result.kind) ? result.kind : void 0;
|
|
6685
|
+
if (outage) {
|
|
6686
|
+
if (providerId === input.providerId) firstOutage ??= outage;
|
|
6687
|
+
outages.report({
|
|
6688
|
+
providerId,
|
|
6689
|
+
kind: outage,
|
|
6690
|
+
...next.action === "fallback" ? { fallbackProviderId: next.providerId } : { fallbacksPaused: allFallbackIds.some((candidate) => candidate !== providerId && outages.pausedKind(candidate) !== void 0) }
|
|
6691
|
+
});
|
|
6692
|
+
}
|
|
6136
6693
|
if (next.action === "fallback") {
|
|
6137
6694
|
logError(`[\u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440] \u0447\u0430\u0442=${input.chatId} \u043C\u043E\u0434\u0435\u043B\u044C=${modelId} \u0441=${providerId} \u043D\u0430=${next.providerId} \u043F\u0440\u0438\u0447\u0438\u043D\u0430=${result.kind} \u043A\u044D\u0448 \u043F\u043E\u0442\u0435\u0440\u044F\u043D`);
|
|
6138
|
-
fallbackUsed = true;
|
|
6695
|
+
if (!outage) fallbackUsed = true;
|
|
6139
6696
|
switchedBySilence = false;
|
|
6140
6697
|
providerId = next.providerId;
|
|
6141
|
-
|
|
6698
|
+
const closing2 = result.contentSeen ? [...openParts.closingChunks(), ...closingChunksForHangingCalls(parts2)] : [];
|
|
6699
|
+
for (const chunk of closing2) emit(chunk);
|
|
6700
|
+
if (closing2.length > 0) answer.continueAfterBreak(parts2, { modelId, providerId: answeredProviderId });
|
|
6701
|
+
else answer.update(parts2, { modelId, providerId: answeredProviderId });
|
|
6142
6702
|
turnUsage.closeAttempt();
|
|
6143
6703
|
if (input.abortSignal.aborted) break;
|
|
6144
6704
|
continue;
|
|
@@ -6210,8 +6770,8 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6210
6770
|
turnUsage.closeAttempt();
|
|
6211
6771
|
continue;
|
|
6212
6772
|
}
|
|
6213
|
-
errorKind = result.kind;
|
|
6214
|
-
errors = attempt.errors;
|
|
6773
|
+
errorKind = errorKindForPerson(result.kind, firstOutage);
|
|
6774
|
+
errors = errorKind === result.kind ? attempt.errors : [{ type: "error", errorText: errorKind }];
|
|
6215
6775
|
terminal = attempt.terminal;
|
|
6216
6776
|
logError(
|
|
6217
6777
|
describeProviderFailure(result.error, { chatId: input.chatId, modelId, providerId, kind: result.kind, env: input.env, catalog: input.catalog })
|
|
@@ -6281,6 +6841,8 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6281
6841
|
const garbled = cutGarbledTail(garbledTail.forCut(parts));
|
|
6282
6842
|
const droppedCalls = garbledTail.droppedCalls();
|
|
6283
6843
|
const garbleLog = `\u0447\u0430\u0442=${input.chatId} \u043E\u0442\u0432\u0435\u0442=${messageId} \u043C\u043E\u0434\u0435\u043B\u044C=${modelId} \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440=${answeredProviderId}`;
|
|
6844
|
+
for (const name of new Set(forgedBlocks.cutBlocks())) logError(`[\u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0439 \u0431\u043B\u043E\u043A \u0432\u044B\u0440\u0435\u0437\u0430\u043D] \u0442\u0435\u0433=${name} ${garbleLog}`);
|
|
6845
|
+
for (const name of new Set(forgedBlocks.keptBlocks())) logError(`[\u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0439 \u0431\u043B\u043E\u043A \u043D\u0435 \u0432\u044B\u0440\u0435\u0437\u0430\u043D: \u043E\u0442\u0432\u0435\u0442 \u043E\u0441\u0442\u0430\u043B\u0441\u044F \u0431\u044B \u043F\u0443\u0441\u0442\u044B\u043C] \u0442\u0435\u0433=${name} ${garbleLog}`);
|
|
6284
6846
|
if (garbled) {
|
|
6285
6847
|
logError(`[\u0445\u0432\u043E\u0441\u0442 \u043E\u0431\u0440\u0435\u0437\u0430\u043D] ${garbleLog} \u043F\u0440\u0438\u0437\u043D\u0430\u043A\u0438=${garbled.signs.join(",")} \u0437\u043D\u0430\u043A\u043E\u0432=${garbled.removedChars} \u0432\u044B\u0437\u043E\u0432\u043E\u0432_\u043D\u0435_\u0437\u0430\u043F\u0443\u0449\u0435\u043D\u043E=${droppedCalls}`);
|
|
6286
6848
|
const cut = garbled.parts.filter((part) => !garbledTail.isDroppedCall(part));
|
|
@@ -6292,7 +6854,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6292
6854
|
const textIndex = lastTextIndex(parts);
|
|
6293
6855
|
if (replyChecks && status === "complete" && textIndex !== -1 && !takeover.signal.aborted) {
|
|
6294
6856
|
const original = parts[textIndex].text;
|
|
6295
|
-
const
|
|
6857
|
+
const checked2 = await checkedReplyText({
|
|
6296
6858
|
checks: replyChecks.checks,
|
|
6297
6859
|
text: original,
|
|
6298
6860
|
ownerId: input.ownerId ?? "",
|
|
@@ -6302,6 +6864,7 @@ async function runTurn(turnInput, emitRaw) {
|
|
|
6302
6864
|
rewrite: createReplyRewrite({ ...input, providerId: answeredProviderId, messageId, logError }),
|
|
6303
6865
|
logError
|
|
6304
6866
|
});
|
|
6867
|
+
const text = checked2 === original ? checked2 : withoutForgedBlocksInRewrite(forgedBlocks.cutText(checked2), checked2, (name) => logError(`[\u0441\u043B\u0443\u0436\u0435\u0431\u043D\u044B\u0439 \u0431\u043B\u043E\u043A \u0432\u044B\u0440\u0435\u0437\u0430\u043D] \u0442\u0435\u0433=${name} ${garbleLog}`));
|
|
6305
6868
|
if (text !== original) {
|
|
6306
6869
|
const corrected = withTextAt(parts, textIndex, text);
|
|
6307
6870
|
if (heldText?.holdsText()) for (const chunk of heldText.release(text)) emitShown(chunk);
|
|
@@ -6559,6 +7122,7 @@ async function openAttempt(input, attempt, attemptSignal) {
|
|
|
6559
7122
|
} = input;
|
|
6560
7123
|
const request = await buildModelRequest({
|
|
6561
7124
|
...requestInput,
|
|
7125
|
+
serviceBlocks: input.serviceBlocks ?? CORE_SERVICE_BLOCKS,
|
|
6562
7126
|
providerId,
|
|
6563
7127
|
history: attempt.history,
|
|
6564
7128
|
frozenContext: { current: { answerId: input.messageId, data: attempt.frozenContext }, group: Boolean(input.group) },
|
|
@@ -6601,9 +7165,11 @@ async function openAttempt(input, attempt, attemptSignal) {
|
|
|
6601
7165
|
};
|
|
6602
7166
|
const { noteAfterApprovals, ...modelRequest } = request;
|
|
6603
7167
|
attempt.garbledTail.reset();
|
|
7168
|
+
attempt.forgedBlocks.reset(lastQuestionOf(attempt.history));
|
|
6604
7169
|
const result = streamText({
|
|
6605
7170
|
...modelRequest,
|
|
6606
|
-
|
|
7171
|
+
// Сначала вырезка блоков, потом проверка на кашу: каша судит уже чистый текст.
|
|
7172
|
+
model: attempt.garbledTail.wrap(attempt.forgedBlocks.wrap(modelRequest.model)),
|
|
6607
7173
|
...approval.toolApproval && { toolApproval: gateToolApproval(approval.toolApproval, () => toolsClosed) },
|
|
6608
7174
|
// Попытке — остаток шагов хода.
|
|
6609
7175
|
// Помощник встал на подтверждении человека — ход ждёт, модель основного агента его незаконченным не видит.
|
|
@@ -6823,6 +7389,7 @@ function helperRunContext(input, toolQueue, logError) {
|
|
|
6823
7389
|
...input.toolTimeout && { toolTimeout: input.toolTimeout },
|
|
6824
7390
|
...input.sequentialTools && { sequentialTools: input.sequentialTools },
|
|
6825
7391
|
...input.providerFallback !== void 0 && { providerFallback: input.providerFallback },
|
|
7392
|
+
...input.providerOutages && { providerOutages: input.providerOutages },
|
|
6826
7393
|
...input.modelSilence && { modelSilence: input.modelSilence },
|
|
6827
7394
|
...input.turnObserver && { turnObserver: input.turnObserver },
|
|
6828
7395
|
...input.assertWithinLimit && { assertWithinLimit: input.assertWithinLimit }
|
|
@@ -7025,6 +7592,11 @@ function emitReply(text, emit) {
|
|
|
7025
7592
|
emit({ type: "text-end", id });
|
|
7026
7593
|
emit({ type: "finish-step" });
|
|
7027
7594
|
}
|
|
7595
|
+
function withoutForgedBlocksInRewrite(cut, text, logCut) {
|
|
7596
|
+
if (cut.blocks.length === 0 || !/[\p{L}\p{N}]/u.test(cut.text)) return text;
|
|
7597
|
+
for (const name of new Set(cut.blocks)) logCut(name);
|
|
7598
|
+
return cut.text;
|
|
7599
|
+
}
|
|
7028
7600
|
|
|
7029
7601
|
// src/helpers/run-helper.ts
|
|
7030
7602
|
var DEFAULT_HELPER_MAX_STEPS = 10;
|
|
@@ -7053,16 +7625,26 @@ async function* helperSteps(input) {
|
|
|
7053
7625
|
const messages = [...input.priorMessages ?? []];
|
|
7054
7626
|
const maxSteps = helper.maxSteps ?? DEFAULT_HELPER_MAX_STEPS;
|
|
7055
7627
|
const { modelId } = helper.model;
|
|
7056
|
-
const
|
|
7628
|
+
const outages = context.providerOutages ?? createProviderOutages({ now: Date.now, logError: context.logError });
|
|
7629
|
+
const allFallbackIds = fallbackProviders({
|
|
7057
7630
|
catalog: context.catalog,
|
|
7058
7631
|
modelId,
|
|
7059
7632
|
chosenProviderId: helper.model.providerId,
|
|
7060
7633
|
env: context.env,
|
|
7061
7634
|
...context.providerFallback !== void 0 && { setting: context.providerFallback }
|
|
7062
7635
|
});
|
|
7636
|
+
const fallbackProviderIds = allFallbackIds.filter((candidate) => outages.pausedKind(candidate) === void 0);
|
|
7063
7637
|
const toolQueue = context.toolQueue ?? createToolQueue();
|
|
7064
7638
|
let providerId = helper.model.providerId;
|
|
7065
7639
|
let fallbackUsed = false;
|
|
7640
|
+
let firstOutage;
|
|
7641
|
+
const primaryPaused = outages.admit(providerId);
|
|
7642
|
+
if (primaryPaused !== void 0 && fallbackProviderIds[0] !== void 0) {
|
|
7643
|
+
context.logError(
|
|
7644
|
+
`[\u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440] \u0447\u0430\u0442=${context.chatId} \u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A=${input.name} \u043C\u043E\u0434\u0435\u043B\u044C=${modelId} \u0441=${providerId} \u043D\u0430=${fallbackProviderIds[0]} \u043F\u0440\u0438\u0447\u0438\u043D\u0430=${primaryPaused} \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u0439 \u043D\u0430 \u043F\u0430\u0443\u0437\u0435`
|
|
7645
|
+
);
|
|
7646
|
+
providerId = fallbackProviderIds[0];
|
|
7647
|
+
}
|
|
7066
7648
|
let externalContent = false;
|
|
7067
7649
|
const report = (status, extra = {}) => ({
|
|
7068
7650
|
messages: [...messages],
|
|
@@ -7089,17 +7671,28 @@ async function* helperSteps(input) {
|
|
|
7089
7671
|
} catch (error) {
|
|
7090
7672
|
if (abortSignal.aborted) throw error;
|
|
7091
7673
|
const kind = classifyProviderError(error, provider);
|
|
7092
|
-
const
|
|
7674
|
+
const candidates = providerId === helper.model.providerId ? fallbackProviderIds : fallbackProviderIds.slice(fallbackProviderIds.indexOf(providerId) + 1);
|
|
7675
|
+
const next = decideNextAttempt({ outcome: "failed", kind, error, contentSeen: false }, { fallbackUsed, fallbackProviderIds: candidates });
|
|
7676
|
+
const outage = isProviderOutage(kind) ? kind : void 0;
|
|
7677
|
+
if (outage) {
|
|
7678
|
+
if (providerId === helper.model.providerId) firstOutage ??= outage;
|
|
7679
|
+
outages.report({
|
|
7680
|
+
providerId,
|
|
7681
|
+
kind: outage,
|
|
7682
|
+
...next.action === "fallback" ? { fallbackProviderId: next.providerId } : { fallbacksPaused: allFallbackIds.some((candidate) => candidate !== providerId && outages.pausedKind(candidate) !== void 0) }
|
|
7683
|
+
});
|
|
7684
|
+
}
|
|
7093
7685
|
if (next.action === "fallback") {
|
|
7094
7686
|
context.logError(`[\u0437\u0430\u043F\u0430\u0441\u043D\u043E\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440] \u0447\u0430\u0442=${context.chatId} \u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A=${input.name} \u043C\u043E\u0434\u0435\u043B\u044C=${modelId} \u0441=${providerId} \u043D\u0430=${next.providerId} \u043F\u0440\u0438\u0447\u0438\u043D\u0430=${kind}`);
|
|
7095
|
-
fallbackUsed = true;
|
|
7687
|
+
if (!outage) fallbackUsed = true;
|
|
7096
7688
|
providerId = next.providerId;
|
|
7097
7689
|
continue;
|
|
7098
7690
|
}
|
|
7099
7691
|
context.logError(describeProviderFailure(error, { chatId: context.chatId, modelId, providerId, kind, env: context.env, catalog: context.catalog }));
|
|
7100
|
-
yield report("failed", { error: FAILURE_REASONS[kind] });
|
|
7692
|
+
yield report("failed", { error: FAILURE_REASONS[errorKindForPerson(kind, firstOutage)] });
|
|
7101
7693
|
return;
|
|
7102
7694
|
}
|
|
7695
|
+
outages.recovered(providerId);
|
|
7103
7696
|
messages.push(...response);
|
|
7104
7697
|
externalContent ||= callsExternalContentTool(messages, tools);
|
|
7105
7698
|
const pendingApproval = pendingApprovalOf2(messages, tools);
|
|
@@ -7691,7 +8284,7 @@ var fireworksProvider = createFireworksProvider();
|
|
|
7691
8284
|
|
|
7692
8285
|
// src/models/providers/openrouter.ts
|
|
7693
8286
|
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
|
7694
|
-
import { wrapLanguageModel as
|
|
8287
|
+
import { wrapLanguageModel as wrapLanguageModel6 } from "ai";
|
|
7695
8288
|
|
|
7696
8289
|
// src/models/providers/openrouter-cache-mark-limit.ts
|
|
7697
8290
|
function openrouterCacheMarkLimit(providerModelId) {
|
|
@@ -7941,7 +8534,7 @@ function createOpenRouterProvider(options2 = {}) {
|
|
|
7941
8534
|
createLanguageModel: (providerModelId, apiKey, requestOptions) => {
|
|
7942
8535
|
const markLimit = openrouterCacheMarkLimit(providerModelId);
|
|
7943
8536
|
const fetch = markLimit === void 0 ? requestOptions?.fetch : fetchWithCacheMarkLimit(requestOptions?.fetch, markLimit);
|
|
7944
|
-
return
|
|
8537
|
+
return wrapLanguageModel6({
|
|
7945
8538
|
model: createOpenRouter({ apiKey, fetch, ...connection }).chat(providerModelId, routingOptions),
|
|
7946
8539
|
middleware: [
|
|
7947
8540
|
openrouterRoutingMiddleware(routing),
|
|
@@ -9843,7 +10436,7 @@ function isPlainObject3(value) {
|
|
|
9843
10436
|
}
|
|
9844
10437
|
|
|
9845
10438
|
// src/memory/memory-after-answer.ts
|
|
9846
|
-
import { wrapLanguageModel as
|
|
10439
|
+
import { wrapLanguageModel as wrapLanguageModel7 } from "ai";
|
|
9847
10440
|
var DRAIN_WAIT_MS = 5e3;
|
|
9848
10441
|
function createMemoryAfterAnswer({ memory, storage, catalog, env, logError, turnObserver }) {
|
|
9849
10442
|
const running = /* @__PURE__ */ new Set();
|
|
@@ -9888,7 +10481,7 @@ function createMemoryAfterAnswer({ memory, storage, catalog, env, logError, turn
|
|
|
9888
10481
|
};
|
|
9889
10482
|
}
|
|
9890
10483
|
function withRetries(model, context) {
|
|
9891
|
-
return typeof model === "string" ? model :
|
|
10484
|
+
return typeof model === "string" ? model : wrapLanguageModel7({ model, middleware: createTransientRetryMiddleware(context) });
|
|
9892
10485
|
}
|
|
9893
10486
|
|
|
9894
10487
|
// src/conversation/to-agent-chat-message.ts
|
|
@@ -10143,6 +10736,7 @@ function createCompactionService(options2) {
|
|
|
10143
10736
|
...observation(ownerId, answerId),
|
|
10144
10737
|
history,
|
|
10145
10738
|
...options2.attachments && { attachments: options2.attachments },
|
|
10739
|
+
...options2.serviceBlocks && { serviceBlocks: options2.serviceBlocks },
|
|
10146
10740
|
settings,
|
|
10147
10741
|
logError,
|
|
10148
10742
|
abortSignal: signal,
|
|
@@ -10286,6 +10880,7 @@ function createCompactionService(options2) {
|
|
|
10286
10880
|
...observation(ownerId, answerId),
|
|
10287
10881
|
history: [...history, { id: `${chatId}-answer-steps`, role: "assistant", parts: steps }],
|
|
10288
10882
|
...options2.attachments && { attachments: options2.attachments },
|
|
10883
|
+
...options2.serviceBlocks && { serviceBlocks: options2.serviceBlocks },
|
|
10289
10884
|
settings,
|
|
10290
10885
|
task: answerStepsTask(settings.summaryCeilingTokens),
|
|
10291
10886
|
logError,
|
|
@@ -11035,6 +11630,7 @@ var InvalidWidgetAnswerError = class extends Error {
|
|
|
11035
11630
|
}
|
|
11036
11631
|
};
|
|
11037
11632
|
var FAR_FUTURE_ISO = "9999-12-31T23:59:59.999Z";
|
|
11633
|
+
var abandonedBurstReceivers = /* @__PURE__ */ new WeakMap();
|
|
11038
11634
|
var TurnEndedBeforeSubscribeError = class extends Error {
|
|
11039
11635
|
constructor(chatId) {
|
|
11040
11636
|
super(`\u0425\u043E\u0434 \u0432 \u0447\u0430\u0442\u0435 ${chatId} \u0437\u0430\u043A\u043E\u043D\u0447\u0438\u043B\u0441\u044F \u0434\u043E \u043F\u043E\u0434\u043F\u0438\u0441\u043A\u0438`);
|
|
@@ -11145,6 +11741,13 @@ function createChatService(input) {
|
|
|
11145
11741
|
const logError = input.logError ?? ((message) => console.error(message));
|
|
11146
11742
|
const now = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
11147
11743
|
const replyChecks = checkedReplyChecks(input.replyChecks);
|
|
11744
|
+
const serviceBlocks = [
|
|
11745
|
+
...CORE_SERVICE_BLOCKS,
|
|
11746
|
+
...input.memory?.serviceBlocks ?? [],
|
|
11747
|
+
...input.attachments?.serviceBlocks ?? [],
|
|
11748
|
+
...input.agent.serviceBlocks ?? []
|
|
11749
|
+
];
|
|
11750
|
+
checkServiceBlockFormats(serviceBlocks);
|
|
11148
11751
|
const widgets = createAgentWidgets(input.agent.widgets);
|
|
11149
11752
|
checkWidgetNamesFree(widgets, input.memory, input.background);
|
|
11150
11753
|
const browserToolDescriptions = checkedBrowserTools(input.agent, input.memory, widgets);
|
|
@@ -11232,6 +11835,7 @@ function createChatService(input) {
|
|
|
11232
11835
|
const modelSilence = resolveModelSilence(modelSilenceTimeout);
|
|
11233
11836
|
const cacheMissWatch = cacheMissLog === false ? void 0 : createCacheMissWatch({ logError });
|
|
11234
11837
|
checkProviderFallback(providerFallback, options2.catalog);
|
|
11838
|
+
const providerOutages = createProviderOutages({ now: () => now().getTime(), logError });
|
|
11235
11839
|
const placeMaxLength = resolvePlaceMaxLength(placeMaxLengthSetting);
|
|
11236
11840
|
const browserToolTimeoutMs = resolveBrowserToolTimeoutMs(browserToolTimeoutSetting);
|
|
11237
11841
|
const pendingBrowserCalls = createPendingBrowserCalls();
|
|
@@ -11292,6 +11896,7 @@ function createChatService(input) {
|
|
|
11292
11896
|
// Сжатие берёт кэш хода: у него те же инструменты, что у модели агента, — с браузерными и помощниками.
|
|
11293
11897
|
agent: { ...options2.agent, tools: withBrowserDescriptions(options2.agent.tools, describedOnlyTools(browserToolDescriptions, options2.agent.helpers, toolResultLimit)) },
|
|
11294
11898
|
...options2.attachments && { attachments: options2.attachments },
|
|
11899
|
+
serviceBlocks,
|
|
11295
11900
|
settings: resolveCompactionSettings(options2.compaction),
|
|
11296
11901
|
logError,
|
|
11297
11902
|
...options2.turnObserver && { turnObserver: options2.turnObserver },
|
|
@@ -11907,6 +12512,7 @@ function createChatService(input) {
|
|
|
11907
12512
|
modelSilence,
|
|
11908
12513
|
...limits.maxOutputTokens !== void 0 && { maxOutputTokens: limits.maxOutputTokens },
|
|
11909
12514
|
...providerFallback !== void 0 && { providerFallback },
|
|
12515
|
+
providerOutages,
|
|
11910
12516
|
...cacheMissWatch && { cacheMissWatch },
|
|
11911
12517
|
...options2.turnObserver && { turnObserver: options2.turnObserver },
|
|
11912
12518
|
...options2.limits && { assertWithinLimit: () => assertWithinSpendingLimit(chat.ownerId, chatId) },
|
|
@@ -11954,6 +12560,7 @@ function createChatService(input) {
|
|
|
11954
12560
|
browserTools: { descriptions: browserToolDescriptions, pending: pendingBrowserCalls, timeoutMs: browserToolTimeoutMs, tabId: launch.page?.tabId }
|
|
11955
12561
|
},
|
|
11956
12562
|
...options2.attachments && { attachments: options2.attachments },
|
|
12563
|
+
serviceBlocks,
|
|
11957
12564
|
...options2.memory && { memory: options2.memory },
|
|
11958
12565
|
...widgets.waiting.size > 0 && { waitingWidgets: widgets.waiting },
|
|
11959
12566
|
...widgets.switchable.size > 0 && { switchableTools: widgets.switchable },
|
|
@@ -12229,10 +12836,10 @@ function createChatService(input) {
|
|
|
12229
12836
|
...page && { page }
|
|
12230
12837
|
});
|
|
12231
12838
|
}
|
|
12232
|
-
function renewingBurstHold(chatId, messageId, dueAt) {
|
|
12839
|
+
function renewingBurstHold(chatId, messageId, dueAt, holder) {
|
|
12233
12840
|
const timer = setInterval(() => {
|
|
12234
12841
|
const heldUntil = new Date(Math.max(now().getTime(), dueAt) + BURST_HOLD_MS).toISOString();
|
|
12235
|
-
storage.renewPendingBurst({ chatId, messageId, holder
|
|
12842
|
+
storage.renewPendingBurst({ chatId, messageId, holder, heldUntil }).then(
|
|
12236
12843
|
(held) => {
|
|
12237
12844
|
if (!held) clearInterval(timer);
|
|
12238
12845
|
},
|
|
@@ -12246,7 +12853,7 @@ function createChatService(input) {
|
|
|
12246
12853
|
function answeringRecordedBurst(ownerId, chatId, questionId, page, burst) {
|
|
12247
12854
|
const unattended = burst.kept && burst.unattended === true;
|
|
12248
12855
|
if (!burst.kept || !keepsPendingBursts) return burstWaits.holding(chatId, () => answeringBurst(ownerId, chatId, questionId, page, unattended));
|
|
12249
|
-
const stopRenewing = renewingBurstHold(chatId, questionId, burst.dueAt);
|
|
12856
|
+
const stopRenewing = renewingBurstHold(chatId, questionId, burst.dueAt, burst.holder ?? copyId);
|
|
12250
12857
|
const answering = burstWaits.holding(chatId, () => answeringBurst(ownerId, chatId, questionId, page, unattended)).finally(stopRenewing);
|
|
12251
12858
|
void answering.then(
|
|
12252
12859
|
() => true,
|
|
@@ -12272,6 +12879,37 @@ function createChatService(input) {
|
|
|
12272
12879
|
const why = error instanceof TurnRefusedError ? `\u043E\u0442\u043A\u0430\u0437 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F ${error.code}: ${error.message}` : error instanceof Error ? error.message : String(error);
|
|
12273
12880
|
logError(`\u0425\u043E\u0434 \u043D\u0430 \u0431\u0440\u043E\u0448\u0435\u043D\u043D\u0443\u044E \u043F\u0430\u0447\u043A\u0443 \u0447\u0430\u0442\u0430 ${chatId} \u043D\u0435 \u043D\u0430\u0447\u0430\u043B\u0441\u044F: ${why}`);
|
|
12274
12881
|
}
|
|
12882
|
+
async function receivingAbandonedBurst(burst) {
|
|
12883
|
+
if (burstWaits.has(burst.chatId)) return;
|
|
12884
|
+
await answeringRecordedBurst(burst.ownerId, burst.chatId, burst.messageId, burst.page, { kept: true, dueAt: Date.parse(burst.dueAt), unattended: true, holder: burst.holder }).then(
|
|
12885
|
+
() => {
|
|
12886
|
+
},
|
|
12887
|
+
(error) => reportingAbandonedBurst(burst.chatId, error)
|
|
12888
|
+
);
|
|
12889
|
+
}
|
|
12890
|
+
async function routingAbandonedBurst(burst) {
|
|
12891
|
+
const serviceFor = options2.abandonedBursts?.serviceFor;
|
|
12892
|
+
if (!serviceFor) return receivingAbandonedBurst(burst);
|
|
12893
|
+
let target;
|
|
12894
|
+
try {
|
|
12895
|
+
const chat = await storage.getChat(burst.chatId);
|
|
12896
|
+
if (!chat) return receivingAbandonedBurst(burst);
|
|
12897
|
+
target = await serviceFor({ ownerId: burst.ownerId, chatId: burst.chatId, chat });
|
|
12898
|
+
} catch (error) {
|
|
12899
|
+
logError(`\u0421\u043B\u0443\u0436\u0431\u0430 \u0434\u043B\u044F \u0431\u0440\u043E\u0448\u0435\u043D\u043D\u043E\u0439 \u043F\u0430\u0447\u043A\u0438 \u0447\u0430\u0442\u0430 ${burst.chatId} \u043D\u0435 \u043D\u0430\u0448\u043B\u0430\u0441\u044C (serviceFor): ${error instanceof Error ? error.message : String(error)} \u2014 \u043F\u0430\u0447\u043A\u0430 \u0436\u0434\u0451\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043E\u0431\u0445\u043E\u0434\u0430`);
|
|
12900
|
+
return;
|
|
12901
|
+
}
|
|
12902
|
+
if (!target) {
|
|
12903
|
+
logError(`\u0414\u043B\u044F \u0431\u0440\u043E\u0448\u0435\u043D\u043D\u043E\u0439 \u043F\u0430\u0447\u043A\u0438 \u0447\u0430\u0442\u0430 ${burst.chatId} \u043D\u0435\u0442 \u0441\u043B\u0443\u0436\u0431\u044B (serviceFor \u2192 ${String(target)}): \u043F\u0430\u0447\u043A\u0430 \u0436\u0434\u0451\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043E\u0431\u0445\u043E\u0434\u0430`);
|
|
12904
|
+
return;
|
|
12905
|
+
}
|
|
12906
|
+
const receive = abandonedBurstReceivers.get(target);
|
|
12907
|
+
if (!receive) {
|
|
12908
|
+
logError(`serviceFor \u0434\u043B\u044F \u0431\u0440\u043E\u0448\u0435\u043D\u043D\u043E\u0439 \u043F\u0430\u0447\u043A\u0438 \u0447\u0430\u0442\u0430 ${burst.chatId} \u0432\u0435\u0440\u043D\u0443\u043B \u043D\u0435 \u0441\u043B\u0443\u0436\u0431\u0443 \u0447\u0430\u0442\u0430 SDK: \u043F\u0430\u0447\u043A\u0430 \u0436\u0434\u0451\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u043E\u0431\u0445\u043E\u0434\u0430`);
|
|
12909
|
+
return;
|
|
12910
|
+
}
|
|
12911
|
+
await receive(burst);
|
|
12912
|
+
}
|
|
12275
12913
|
function sweepingBursts(take) {
|
|
12276
12914
|
if (!keepsPendingBursts || closing) return Promise.resolve();
|
|
12277
12915
|
const sweep = (async () => {
|
|
@@ -12286,9 +12924,8 @@ function createChatService(input) {
|
|
|
12286
12924
|
const taken = await storage.takeAbandonedBursts({ ...take, holder: copyId, heldUntil, limit: free });
|
|
12287
12925
|
if (taken.length === 0) return;
|
|
12288
12926
|
for (const burst of taken) {
|
|
12289
|
-
if (burstWaits.has(burst.chatId)) continue;
|
|
12290
12927
|
burstDecisions++;
|
|
12291
|
-
|
|
12928
|
+
void routingAbandonedBurst(burst).finally(releasingBurstDecision);
|
|
12292
12929
|
}
|
|
12293
12930
|
}
|
|
12294
12931
|
})().catch((error) => logError(`\u041E\u0431\u0445\u043E\u0434 \u0436\u0434\u0443\u0449\u0438\u0445 \u043F\u0430\u0447\u0435\u043A \u043D\u0435 \u0443\u0434\u0430\u043B\u0441\u044F: ${error instanceof Error ? error.message : String(error)}`)).finally(() => burstSweeps.delete(sweep));
|
|
@@ -13203,6 +13840,7 @@ function createChatService(input) {
|
|
|
13203
13840
|
modelSilence,
|
|
13204
13841
|
logError,
|
|
13205
13842
|
...providerFallback !== void 0 && { providerFallback },
|
|
13843
|
+
providerOutages,
|
|
13206
13844
|
...options2.turnObserver && { turnObserver: options2.turnObserver },
|
|
13207
13845
|
...options2.limits && { assertWithinLimit: () => assertWithinSpendingLimit(ownerId, chatId) }
|
|
13208
13846
|
});
|
|
@@ -13212,6 +13850,7 @@ function createChatService(input) {
|
|
|
13212
13850
|
helpers: backgroundHelpers
|
|
13213
13851
|
};
|
|
13214
13852
|
}
|
|
13853
|
+
abandonedBurstReceivers.set(service, receivingAbandonedBurst);
|
|
13215
13854
|
return service;
|
|
13216
13855
|
function isDetachedAnswer(last, runningId) {
|
|
13217
13856
|
return runningId === null && last?.status === "streaming" && !liveAnswerIds.has(last.id);
|
|
@@ -13374,6 +14013,10 @@ export {
|
|
|
13374
14013
|
BURST_HOLD_MS,
|
|
13375
14014
|
BURST_SWEEP_LIMIT,
|
|
13376
14015
|
isBurstMessage,
|
|
14016
|
+
CORE_SERVICE_BLOCKS,
|
|
14017
|
+
serviceBlockName,
|
|
14018
|
+
checkServiceBlockFormats,
|
|
14019
|
+
cutForgedBlocks,
|
|
13377
14020
|
RELEASED_ANSWER_ALIVE_AT,
|
|
13378
14021
|
aliveAtByLocalClock,
|
|
13379
14022
|
silenceBoundaryByLocalClock,
|
|
@@ -13473,4 +14116,4 @@ export {
|
|
|
13473
14116
|
InvalidWidgetAnswerError,
|
|
13474
14117
|
createChatService
|
|
13475
14118
|
};
|
|
13476
|
-
//# sourceMappingURL=chunk-
|
|
14119
|
+
//# sourceMappingURL=chunk-QA5DVEXF.js.map
|