@thesashadev/girl-agent 0.1.7 → 0.1.8
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 +8 -0
- package/README.md +17 -1
- package/dist/cli.js +175 -19
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.8 — OpenAI-compatible API compatibility
|
|
4
|
+
|
|
5
|
+
Дата: 2026-05-06
|
|
6
|
+
|
|
7
|
+
- JSON-ответы теперь сначала запрашиваются через `json_schema`, с fallback на `json_object` и `text` для разных OpenAI-compatible API. (#33)
|
|
8
|
+
- LM Studio и Ollama больше не требуют реальный API ключ в wizard/headless setup.
|
|
9
|
+
- Добавлена совместимость с OpenAI-compatible прокси, которые возвращают SSE/event-stream даже на обычный chat completions запрос.
|
|
10
|
+
|
|
3
11
|
## 0.1.7 — MarkdownV2 escaping fix
|
|
4
12
|
|
|
5
13
|
Дата: 2026-05-06
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+

|
|
2
2
|
|
|
3
3
|
[website]: https://girl-agent.com
|
|
4
4
|
[docs]: https://docs.girl-agent.com
|
|
@@ -49,6 +49,22 @@ Wizard задаст пару вопросов — имя, возраст, Telegr
|
|
|
49
49
|
npx @thesashadev/girl-agent --profile=arina
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
**Через Docker (рекомендуется для сервера):**
|
|
53
|
+
|
|
54
|
+
Первый запуск с интерактивом (для настройки через визард):
|
|
55
|
+
```bash
|
|
56
|
+
docker-compose run --rm -it girl-agent
|
|
57
|
+
```
|
|
58
|
+
*(пройдите все шаги и после появления дашборда нажмите `Ctrl+C`)*
|
|
59
|
+
|
|
60
|
+
Последующие (запуск в фоне):
|
|
61
|
+
```bash
|
|
62
|
+
docker-compose up -d
|
|
63
|
+
```
|
|
64
|
+
*(если профилей несколько, запустите конкретный так: `docker-compose run -d girl-agent node dist/cli.js --profile=arina`)*
|
|
65
|
+
|
|
66
|
+
*(посмотреть логи: `docker-compose logs -f`)*
|
|
67
|
+
|
|
52
68
|
**Из исходников:**
|
|
53
69
|
|
|
54
70
|
```powershell
|
package/dist/cli.js
CHANGED
|
@@ -377,8 +377,10 @@ var LLM_PRESETS = [
|
|
|
377
377
|
proto: "openai",
|
|
378
378
|
baseURL: "http://localhost:1234/v1",
|
|
379
379
|
defaultModel: "",
|
|
380
|
+
defaultApiKey: "lm-studio",
|
|
381
|
+
apiKeyRequired: false,
|
|
380
382
|
custom: true,
|
|
381
|
-
hint: "\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E, OpenAI-compatible endpoint"
|
|
383
|
+
hint: "\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E, OpenAI-compatible endpoint; \u043A\u043B\u044E\u0447 \u043D\u0435 \u043D\u0443\u0436\u0435\u043D"
|
|
382
384
|
},
|
|
383
385
|
{
|
|
384
386
|
id: "ollama",
|
|
@@ -386,8 +388,10 @@ var LLM_PRESETS = [
|
|
|
386
388
|
proto: "openai",
|
|
387
389
|
baseURL: "http://localhost:11434/v1",
|
|
388
390
|
defaultModel: "llama3.1",
|
|
391
|
+
defaultApiKey: "ollama",
|
|
392
|
+
apiKeyRequired: false,
|
|
389
393
|
custom: true,
|
|
390
|
-
hint: "\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E \u0447\u0435\u0440\u0435\u0437 /v1"
|
|
394
|
+
hint: "\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E \u0447\u0435\u0440\u0435\u0437 /v1; \u043A\u043B\u044E\u0447 \u043D\u0435 \u043D\u0443\u0436\u0435\u043D"
|
|
391
395
|
},
|
|
392
396
|
{
|
|
393
397
|
id: "anthropic",
|
|
@@ -1025,20 +1029,28 @@ var OpenAILike = class {
|
|
|
1025
1029
|
constructor(cfg) {
|
|
1026
1030
|
this.cfg = cfg;
|
|
1027
1031
|
this.client = new OpenAI({
|
|
1028
|
-
apiKey: cfg
|
|
1032
|
+
apiKey: openAIApiKey(cfg),
|
|
1029
1033
|
baseURL: normalizeBaseURL(cfg.baseURL),
|
|
1030
1034
|
timeout: LLM_TIMEOUT_MS,
|
|
1031
1035
|
maxRetries: LLM_MAX_RETRIES
|
|
1032
1036
|
});
|
|
1037
|
+
this.fetchClient = new OpenAI({
|
|
1038
|
+
apiKey: openAIApiKey(cfg),
|
|
1039
|
+
baseURL: normalizeBaseURL(cfg.baseURL),
|
|
1040
|
+
timeout: LLM_TIMEOUT_MS,
|
|
1041
|
+
maxRetries: LLM_MAX_RETRIES,
|
|
1042
|
+
fetch: compatibleFetch
|
|
1043
|
+
});
|
|
1033
1044
|
}
|
|
1034
1045
|
cfg;
|
|
1035
1046
|
client;
|
|
1047
|
+
fetchClient;
|
|
1036
1048
|
async chat(messages, opts = {}) {
|
|
1037
1049
|
const params = {
|
|
1038
1050
|
model: this.cfg.model,
|
|
1039
1051
|
messages: openAIMessages(messages),
|
|
1040
1052
|
temperature: opts.temperature ?? 0.85,
|
|
1041
|
-
response_format: opts
|
|
1053
|
+
response_format: openAIResponseFormat(opts)
|
|
1042
1054
|
};
|
|
1043
1055
|
if (usesMaxCompletionTokens(this.cfg.model)) {
|
|
1044
1056
|
params.max_completion_tokens = opts.maxTokens ?? 600;
|
|
@@ -1049,19 +1061,86 @@ var OpenAILike = class {
|
|
|
1049
1061
|
return res.choices[0]?.message?.content?.trim() ?? "";
|
|
1050
1062
|
}
|
|
1051
1063
|
async createWithCompatibilityFallback(params) {
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1064
|
+
const attempted = /* @__PURE__ */ new Set();
|
|
1065
|
+
let current = params;
|
|
1066
|
+
let lastError;
|
|
1067
|
+
while (current) {
|
|
1068
|
+
const key = completionParamsKey(current);
|
|
1069
|
+
if (attempted.has(key)) break;
|
|
1070
|
+
attempted.add(key);
|
|
1057
1071
|
try {
|
|
1058
|
-
return await this.client.chat.completions.create(
|
|
1059
|
-
} catch (
|
|
1060
|
-
|
|
1072
|
+
return await this.client.chat.completions.create(current);
|
|
1073
|
+
} catch (error) {
|
|
1074
|
+
lastError = error;
|
|
1075
|
+
const next = completionFallback(current, error);
|
|
1076
|
+
if (!next) break;
|
|
1077
|
+
current = next;
|
|
1061
1078
|
}
|
|
1062
1079
|
}
|
|
1080
|
+
if (this.cfg.baseURL) {
|
|
1081
|
+
try {
|
|
1082
|
+
return await this.fetchClient.chat.completions.create({ ...params, stream: false });
|
|
1083
|
+
} catch (fetchError) {
|
|
1084
|
+
lastError = fetchError;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
throw enrichOpenAIError(lastError, this.cfg.baseURL);
|
|
1063
1088
|
}
|
|
1064
1089
|
};
|
|
1090
|
+
async function compatibleFetch(url, init) {
|
|
1091
|
+
const res = await fetch(url, init);
|
|
1092
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
1093
|
+
if (!res.ok) return res;
|
|
1094
|
+
const text = await res.clone().text();
|
|
1095
|
+
if (contentType.includes("text/event-stream") || text.trimStart().startsWith("data:")) {
|
|
1096
|
+
return completionStreamToJsonResponse(res, text);
|
|
1097
|
+
}
|
|
1098
|
+
return res;
|
|
1099
|
+
}
|
|
1100
|
+
function completionStreamToJsonResponse(res, text) {
|
|
1101
|
+
const completion = parseOpenAIEventStream(text);
|
|
1102
|
+
return new Response(JSON.stringify(completion), {
|
|
1103
|
+
status: res.status,
|
|
1104
|
+
statusText: res.statusText,
|
|
1105
|
+
headers: { "content-type": "application/json" }
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
function parseOpenAIEventStream(raw) {
|
|
1109
|
+
let id = "chatcmpl-stream";
|
|
1110
|
+
let model = "";
|
|
1111
|
+
let created = Math.floor(Date.now() / 1e3);
|
|
1112
|
+
const content = [];
|
|
1113
|
+
let finishReason = "stop";
|
|
1114
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
1115
|
+
const trimmed = line.trim();
|
|
1116
|
+
if (!trimmed.startsWith("data:")) continue;
|
|
1117
|
+
const data = trimmed.slice(5).trim();
|
|
1118
|
+
if (!data || data === "[DONE]") continue;
|
|
1119
|
+
try {
|
|
1120
|
+
const chunk = JSON.parse(data);
|
|
1121
|
+
id = chunk.id || id;
|
|
1122
|
+
model = chunk.model || model;
|
|
1123
|
+
created = chunk.created || created;
|
|
1124
|
+
const choice = chunk.choices[0];
|
|
1125
|
+
finishReason = choice?.finish_reason ?? finishReason;
|
|
1126
|
+
const delta = choice?.delta?.content ?? choice?.message?.content;
|
|
1127
|
+
if (typeof delta === "string") content.push(delta);
|
|
1128
|
+
} catch {
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
return {
|
|
1132
|
+
id,
|
|
1133
|
+
object: "chat.completion",
|
|
1134
|
+
created,
|
|
1135
|
+
model,
|
|
1136
|
+
choices: [{
|
|
1137
|
+
index: 0,
|
|
1138
|
+
message: { role: "assistant", content: content.join(""), refusal: null },
|
|
1139
|
+
finish_reason: finishReason,
|
|
1140
|
+
logprobs: null
|
|
1141
|
+
}]
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1065
1144
|
var AnthropicLike = class {
|
|
1066
1145
|
constructor(cfg) {
|
|
1067
1146
|
this.cfg = cfg;
|
|
@@ -1155,8 +1234,36 @@ function normalizeBaseURL(value) {
|
|
|
1155
1234
|
function usesMaxCompletionTokens(model) {
|
|
1156
1235
|
return /^(?:o\d|o\d-|o\d\b|gpt-5|gpt-5\.|gpt-[5-9])|\/(?:o\d|gpt-5|gpt-[5-9])/.test(model.trim().toLowerCase());
|
|
1157
1236
|
}
|
|
1237
|
+
function openAIApiKey(cfg) {
|
|
1238
|
+
return cfg.apiKey.trim() || (cfg.presetId === "ollama" ? "ollama" : cfg.presetId === "lmstudio" ? "lm-studio" : "");
|
|
1239
|
+
}
|
|
1240
|
+
function openAIResponseFormat(opts) {
|
|
1241
|
+
if (!opts.json) return void 0;
|
|
1242
|
+
if (opts.jsonSchema) return { type: "json_schema", json_schema: opts.jsonSchema };
|
|
1243
|
+
return {
|
|
1244
|
+
type: "json_schema",
|
|
1245
|
+
json_schema: {
|
|
1246
|
+
name: "json_response",
|
|
1247
|
+
strict: false,
|
|
1248
|
+
schema: { type: "object", additionalProperties: true }
|
|
1249
|
+
}
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
function completionFallback(params, error) {
|
|
1253
|
+
return responseFormatFallback(params, error) ?? completionTokenFallback(params, error);
|
|
1254
|
+
}
|
|
1255
|
+
function responseFormatFallback(params, error) {
|
|
1256
|
+
const message = openAIErrorText(error);
|
|
1257
|
+
if (!params.response_format || !message.includes("response_format")) return null;
|
|
1258
|
+
if (params.response_format.type === "json_schema" && message.includes("json_object")) {
|
|
1259
|
+
return { ...params, response_format: { type: "text" } };
|
|
1260
|
+
}
|
|
1261
|
+
if (params.response_format.type === "json_schema") return { ...params, response_format: { type: "json_object" } };
|
|
1262
|
+
if (params.response_format.type === "json_object") return { ...params, response_format: { type: "text" } };
|
|
1263
|
+
return null;
|
|
1264
|
+
}
|
|
1158
1265
|
function completionTokenFallback(params, error) {
|
|
1159
|
-
const message =
|
|
1266
|
+
const message = openAIErrorText(error);
|
|
1160
1267
|
if (params.max_tokens != null && message.includes("max_tokens") && message.includes("max_completion_tokens")) {
|
|
1161
1268
|
const { max_tokens, ...rest } = params;
|
|
1162
1269
|
return { ...rest, max_completion_tokens: max_tokens };
|
|
@@ -1167,6 +1274,16 @@ function completionTokenFallback(params, error) {
|
|
|
1167
1274
|
}
|
|
1168
1275
|
return null;
|
|
1169
1276
|
}
|
|
1277
|
+
function completionParamsKey(params) {
|
|
1278
|
+
const tokenKey = params.max_completion_tokens != null ? "max_completion_tokens" : "max_tokens";
|
|
1279
|
+
return `${params.response_format?.type ?? "default"}:${tokenKey}`;
|
|
1280
|
+
}
|
|
1281
|
+
function openAIErrorText(error) {
|
|
1282
|
+
if (error instanceof OpenAI.APIError) {
|
|
1283
|
+
return `${error.status ?? ""} ${error.code ?? ""} ${error.type ?? ""} ${error.message}`.toLowerCase();
|
|
1284
|
+
}
|
|
1285
|
+
return errorMessage(error).toLowerCase();
|
|
1286
|
+
}
|
|
1170
1287
|
function enrichOpenAIError(error, baseURL) {
|
|
1171
1288
|
if (error instanceof OpenAI.APIConnectionError) {
|
|
1172
1289
|
return new Error(connectionErrorMessage("OpenAI-compatible", baseURL, error));
|
|
@@ -1202,6 +1319,40 @@ function makeLLM(cfg) {
|
|
|
1202
1319
|
init_esm_shims();
|
|
1203
1320
|
var SYS = `\u0422\u044B \u2014 \u0440\u0435\u0436\u0438\u0441\u0441\u0451\u0440 \u043F\u0435\u0440\u0441\u043E\u043D\u0430\u0436\u0435\u0439. \u0422\u0432\u043E\u044F \u0437\u0430\u0434\u0430\u0447\u0430 \u2014 \u0441\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0434\u043E\u0441\u0442\u043E\u0432\u0435\u0440\u043D\u044B\u0439, \u043D\u0435-\u0433\u043E\u043B\u043B\u0438\u0432\u0443\u0434\u0441\u043A\u0438\u0439, \u043D\u0435-\u0441\u043B\u0430\u0434\u043A\u0438\u0439, \u043D\u0435-AI-\u043F\u043E\u043C\u043E\u0449\u043D\u0438\u043A\u043E\u0432\u044B\u0439 \u043F\u0440\u043E\u0444\u0438\u043B\u044C \u043E\u0431\u044B\u0447\u043D\u043E\u0439 \u0440\u0443\u0441\u0441\u043A\u043E\u044F\u0437\u044B\u0447\u043D\u043E\u0439 \u0434\u0435\u0432\u0443\u0448\u043A\u0438 \u0443\u043A\u0430\u0437\u0430\u043D\u043D\u043E\u0433\u043E \u0432\u043E\u0437\u0440\u0430\u0441\u0442\u0430. \u0411\u0435\u0437 \u043F\u0440\u0438\u0443\u043A\u0440\u0430\u0448\u0438\u0432\u0430\u043D\u0438\u0439, \u0431\u0435\u0437 \u043F\u043E\u0437\u0438\u0442\u0438\u0432\u043D\u043E\u0433\u043E \u043C\u043E\u0440\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u0443\u043A\u043B\u043E\u043D\u0430, \u0431\u0435\u0437 \xAB\u0443\u0434\u043E\u0431\u043D\u043E\u0439\xBB \u043F\u0435\u0440\u0441\u043E\u043D\u044B. \u0414\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u044B \u043D\u0435\u0434\u043E\u0441\u0442\u0430\u0442\u043A\u0438, \u0443\u0437\u043A\u0438\u0435 \u0438\u043D\u0442\u0435\u0440\u0435\u0441\u044B, \u0441\u043A\u0435\u043F\u0441\u0438\u0441, \u0442\u043E\u043A\u0441\u0438\u0447\u043D\u043E\u0441\u0442\u044C, \u043B\u0435\u043D\u044C, \u0442\u0440\u0435\u0432\u043E\u0436\u043D\u043E\u0441\u0442\u044C, \u043D\u0435\u0443\u0432\u0435\u0440\u0435\u043D\u043D\u043E\u0441\u0442\u044C \u2014 \u0447\u0442\u043E \u043F\u043E\u0434\u0445\u043E\u0434\u0438\u0442 \u0432\u043E\u0437\u0440\u0430\u0441\u0442\u0443. \u041D\u0438\u043A\u0430\u043A\u0438\u0445 \xAB\u043A\u043E\u0443\u0447\u0438\u043D\u0433\u043E\u0432\u044B\u0445\xBB \u0444\u043E\u0440\u043C\u0443\u043B\u0438\u0440\u043E\u0432\u043E\u043A. \u0422\u043E\u043B\u044C\u043A\u043E \u0436\u0438\u0432\u0430\u044F \u0440\u0435\u0447\u044C, \u043A\u0430\u043A \u0438\u0437 \u0434\u043D\u0435\u0432\u043D\u0438\u043A\u0430 \u0438\u043B\u0438 \u0432\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0435\u0433\u043E \u043C\u043E\u043D\u043E\u043B\u043E\u0433\u0430. \u0412\u043E\u0437\u0440\u0430\u0441\u0442: {{age}} \u043B\u0435\u0442, \u0438\u043C\u044F: {{name}}.`;
|
|
1204
1321
|
var WEEKDAYS = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
|
|
1322
|
+
var BUSY_SCHEDULE_SCHEMA = {
|
|
1323
|
+
name: "busy_schedule",
|
|
1324
|
+
strict: false,
|
|
1325
|
+
schema: {
|
|
1326
|
+
type: "object",
|
|
1327
|
+
properties: {
|
|
1328
|
+
busySchedule: {
|
|
1329
|
+
type: "array",
|
|
1330
|
+
items: {
|
|
1331
|
+
type: "object",
|
|
1332
|
+
properties: {
|
|
1333
|
+
label: { type: "string" },
|
|
1334
|
+
days: {
|
|
1335
|
+
type: "array",
|
|
1336
|
+
items: { type: "string", enum: WEEKDAYS }
|
|
1337
|
+
},
|
|
1338
|
+
from: { type: "string" },
|
|
1339
|
+
to: { type: "string" },
|
|
1340
|
+
checkAfterMin: {
|
|
1341
|
+
type: "array",
|
|
1342
|
+
items: { type: "number" },
|
|
1343
|
+
minItems: 2,
|
|
1344
|
+
maxItems: 2
|
|
1345
|
+
}
|
|
1346
|
+
},
|
|
1347
|
+
required: ["label", "from", "to"],
|
|
1348
|
+
additionalProperties: false
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
},
|
|
1352
|
+
required: ["busySchedule"],
|
|
1353
|
+
additionalProperties: false
|
|
1354
|
+
}
|
|
1355
|
+
};
|
|
1205
1356
|
async function generatePersonaPack(llm, slug, name, age, nationality = "RU", personaNotes = "", onProgress) {
|
|
1206
1357
|
const country = nationality === "UA" ? "\u0423\u043A\u0440\u0430\u0438\u043D\u0430" : "\u0420\u043E\u0441\u0441\u0438\u044F / \u0421\u041D\u0413";
|
|
1207
1358
|
const langHint = nationality === "UA" ? "\u041F\u0438\u0448\u0435\u0442 \u043D\u0430 \u0420\u0423\u0421\u0421\u041A\u041E\u041C (\u043A\u0430\u043A \u0440\u0435\u0430\u043B\u044C\u043D\u043E \u043F\u0438\u0448\u0435\u0442 \u0431\u043E\u043B\u044C\u0448\u0438\u043D\u0441\u0442\u0432\u043E \u0434\u0435\u0432\u0443\u0448\u0435\u043A \u0432 \u0423\u043A\u0440\u0430\u0438\u043D\u0435 \u0432 \u0442\u0433). \u0414\u043E\u043F\u0443\u0441\u0442\u0438\u043C \u043B\u0451\u0433\u043A\u0438\u0439 \u0441\u0443\u0440\u0436\u0438\u043A: ~90% \u0440\u0443\u0441\u0441\u043A\u0438\u0439 + ~10% \u0443\u043A\u0440\u0430\u0438\u043D\u0441\u043A\u0438\u0445 \u0432\u0441\u0442\u0430\u0432\u043E\u043A (\u043E\u0442\u0434\u0435\u043B\u044C\u043D\u044B\u0435 \u0441\u043B\u043E\u0432\u0430: '\u0448\u043E', '\u043C\u0430\u0431\u0443\u0442\u044C', '\u0442\u0440\u043E\u0445\u0438', '\u044F\u043A', '\u043D\u0443 \u0448\u043E', '\u0442\u0430 \u0439\u0434\u0438', '\u0434\u044F\u043A\u0443\u044E'), \u043D\u043E \u0431\u0435\u0437 \u043F\u043E\u043B\u043D\u043E\u0433\u043E \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u0430 \u043D\u0430 \u0443\u043A\u0440\u0430\u0438\u043D\u0441\u043A\u0438\u0439. \u0427\u0438\u0441\u0442\u043E-\u0443\u043A\u0440\u0430\u0438\u043D\u0441\u043A\u0438\u0439 \u0442\u0435\u043A\u0441\u0442 \u041D\u0415 \u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0439 \u2014 \u044D\u0442\u043E \u043D\u0435\u0440\u0435\u0430\u043B\u0438\u0441\u0442\u0438\u0447\u043D\u043E \u0434\u043B\u044F \u0442\u0433-\u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0438." : "\u0420\u0443\u0441\u0441\u043A\u043E\u044F\u0437\u044B\u0447\u043D\u0430\u044F \u0431\u0435\u0437 \u0443\u043A\u0440\u0430\u0438\u043D\u0438\u0437\u043C\u043E\u0432.";
|
|
@@ -1312,7 +1463,7 @@ ${personaNotes.trim()}
|
|
|
1312
1463
|
onProgress?.(65, "\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C communication.md\u2026");
|
|
1313
1464
|
const boundaries = await llm.chat([{ role: "system", content: sys }, { role: "user", content: boundariesPrompt }], { temperature: 0.9, maxTokens: 3500 });
|
|
1314
1465
|
onProgress?.(85, "\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C busy schedule\u2026");
|
|
1315
|
-
const routineRaw = await llm.chat([{ role: "system", content: sys }, { role: "user", content: routinePrompt }], { temperature: 0.85, maxTokens: 3500, json: true });
|
|
1466
|
+
const routineRaw = await llm.chat([{ role: "system", content: sys }, { role: "user", content: routinePrompt }], { temperature: 0.85, maxTokens: 3500, json: true, jsonSchema: BUSY_SCHEDULE_SCHEMA });
|
|
1316
1467
|
const busySchedule = parseBusySchedule(routineRaw, name, age);
|
|
1317
1468
|
await writeMd(slug, "persona.md", persona);
|
|
1318
1469
|
await writeMd(slug, "speech.md", speech);
|
|
@@ -1866,6 +2017,7 @@ function Wizard({ initial, onDone }) {
|
|
|
1866
2017
|
setLlmProto(preset.proto);
|
|
1867
2018
|
setLlmBaseURL(preset.baseURL ?? "");
|
|
1868
2019
|
setLlmModel(preset.defaultModel);
|
|
2020
|
+
setLlmKey(preset.defaultApiKey ?? "");
|
|
1869
2021
|
if (preset.custom) setStep("api-base");
|
|
1870
2022
|
else setStep("api-model");
|
|
1871
2023
|
}
|
|
@@ -1893,7 +2045,9 @@ function Wizard({ initial, onDone }) {
|
|
|
1893
2045
|
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u043D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u043C\u043E\u0434\u0435\u043B\u0438" }), /* @__PURE__ */ React.createElement(Bar, { step: 2, total: 9 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Model: "), /* @__PURE__ */ React.createElement(TextInput, { value: llmModel, onChange: setLlmModel, onSubmit: () => setStep("api-key") })));
|
|
1894
2046
|
}
|
|
1895
2047
|
if (step === "api-key") {
|
|
1896
|
-
|
|
2048
|
+
const preset = findPreset(llmPresetId);
|
|
2049
|
+
const apiKeyRequired = preset?.apiKeyRequired !== false;
|
|
2050
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: apiKeyRequired ? "API \u043A\u043B\u044E\u0447" : "API \u043A\u043B\u044E\u0447 (\u043C\u043E\u0436\u043D\u043E \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438\u0442\u044C)" }), /* @__PURE__ */ React.createElement(Bar, { step: 2, total: 11 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Key: "), /* @__PURE__ */ React.createElement(TextInput, { value: llmKey, onChange: setLlmKey, mask: "\u2022", onSubmit: () => (llmKey || !apiKeyRequired) && setStep("nationality") })), !apiKeyRequired && /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "\u0414\u043B\u044F \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E\u0433\u043E API \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D \u0442\u0435\u0445\u043D\u0438\u0447\u0435\u0441\u043A\u0438\u0439 placeholder, \u0435\u0441\u043B\u0438 \u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043F\u0443\u0441\u0442\u043E."));
|
|
1897
2051
|
}
|
|
1898
2052
|
if (step === "nationality") {
|
|
1899
2053
|
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u043D\u0430\u0446\u0438\u043E\u043D\u0430\u043B\u044C\u043D\u043E\u0441\u0442\u044C (\u044F\u0437\u044B\u043A, \u0438\u043C\u044F, \u043A\u0443\u043B\u044C\u0442\u0443\u0440\u0430)" }), /* @__PURE__ */ React.createElement(Bar, { step: 3, total: 11 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(
|
|
@@ -5587,7 +5741,7 @@ usage:
|
|
|
5587
5741
|
npx girl-agent --reset --profile=<slug>
|
|
5588
5742
|
npx girl-agent <flags> # \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u0432\u0438\u0437\u0430\u0440\u0434 \u0441 \u0430\u0440\u0433\u0443\u043C\u0435\u043D\u0442\u0430\u043C\u0438
|
|
5589
5743
|
|
|
5590
|
-
required flags \u0434\u043B\u044F headless setup (--name --age --stage --api-preset --api-key
|
|
5744
|
+
required flags \u0434\u043B\u044F headless setup (--name --age --stage --api-preset --mode; --api-key \u043D\u0443\u0436\u0435\u043D \u0442\u043E\u043B\u044C\u043A\u043E \u0434\u043B\u044F \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u043E\u0432 \u0441 \u0430\u0432\u0442\u043E\u0440\u0438\u0437\u0430\u0446\u0438\u0435\u0439):
|
|
5591
5745
|
--profile=<slug> slug \u043F\u0440\u043E\u0444\u0438\u043B\u044F
|
|
5592
5746
|
--mode=bot|userbot
|
|
5593
5747
|
--token=<bot_token> \u0434\u043B\u044F bot
|
|
@@ -5596,7 +5750,7 @@ required flags \u0434\u043B\u044F headless setup (--name --age --stage --api-pre
|
|
|
5596
5750
|
--base-url=<url> \u0434\u043B\u044F custom
|
|
5597
5751
|
--proto=openai|anthropic \u0434\u043B\u044F custom
|
|
5598
5752
|
--model=<model>
|
|
5599
|
-
--api-key=<key>
|
|
5753
|
+
--api-key=<key> \u043D\u0435 \u043D\u0443\u0436\u0435\u043D \u0434\u043B\u044F \u043B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0445 LM Studio/Ollama
|
|
5600
5754
|
--name=<\u0438\u043C\u044F> \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E\u0435 \u0438\u043C\u044F; \u0435\u0441\u043B\u0438 \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u2014 \u0441\u043B\u0443\u0447\u0430\u0439\u043D\u043E\u0435 \u0438\u0437 \u043F\u0443\u043B\u0430 \u043F\u043E nationality (\u0442\u0443\u0440\u043D\u0438\u0440 \u0432\u044B\u0431\u043E\u0440\u0430 \u0438\u043C\u0451\u043D \u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0422\u041E\u041B\u042C\u041A\u041E \u0432 TUI \u0432\u0438\u0437\u0430\u0440\u0434\u0435)
|
|
5601
5755
|
--age=<n>
|
|
5602
5756
|
--persona-notes=<text> \u0434\u043E\u043F. \u043F\u043E\u0436\u0435\u043B\u0430\u043D\u0438\u044F \u043A persona/speech/communication \u043F\u0435\u0440\u0435\u0434 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0435\u0439
|
|
@@ -5683,7 +5837,9 @@ ${profiles.join("\n")}
|
|
|
5683
5837
|
await runRuntime(cfg);
|
|
5684
5838
|
return;
|
|
5685
5839
|
}
|
|
5686
|
-
const
|
|
5840
|
+
const presetForFlags = argv["api-preset"] ? findPreset(String(argv["api-preset"])) : void 0;
|
|
5841
|
+
const apiKeyRequiredForFlags = presetForFlags?.apiKeyRequired !== false;
|
|
5842
|
+
const haveEnoughForFlags = argv.mode && argv["api-preset"] && (!apiKeyRequiredForFlags || argv["api-key"]) && argv.age && argv.stage;
|
|
5687
5843
|
if (haveEnoughForFlags) {
|
|
5688
5844
|
const cfg = await buildConfigFromFlags(argv);
|
|
5689
5845
|
await writeConfig(cfg);
|
|
@@ -5755,7 +5911,7 @@ async function buildConfigFromFlags(argv) {
|
|
|
5755
5911
|
tz,
|
|
5756
5912
|
mode,
|
|
5757
5913
|
stage: argv.stage,
|
|
5758
|
-
llm: { presetId, proto, baseURL, apiKey: String(argv["api-key"]), model },
|
|
5914
|
+
llm: { presetId, proto, baseURL, apiKey: String(argv["api-key"] ?? preset?.defaultApiKey ?? ""), model },
|
|
5759
5915
|
telegram: mode === "bot" ? { botToken: String(argv.token ?? "") } : {
|
|
5760
5916
|
apiId: Number(argv["api-id"] ?? 0),
|
|
5761
5917
|
apiHash: String(argv["api-hash"] ?? ""),
|