Improve pilot chat live traces and remove mock placeholders
This commit is contained in:
@@ -190,7 +190,6 @@ type PilotMessage = {
|
|||||||
at: string;
|
at: string;
|
||||||
}> | null;
|
}> | null;
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
pending?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type ChatConversation = {
|
type ChatConversation = {
|
||||||
@@ -222,23 +221,6 @@ const loginPassword = ref("");
|
|||||||
const loginError = ref<string | null>(null);
|
const loginError = ref<string | null>(null);
|
||||||
const loginBusy = ref(false);
|
const loginBusy = ref(false);
|
||||||
|
|
||||||
const pilotTimeline = computed<PilotMessage[]>(() => {
|
|
||||||
if (!pilotSending.value) return pilotMessages.value;
|
|
||||||
return [
|
|
||||||
...pilotMessages.value,
|
|
||||||
{
|
|
||||||
id: "__pilot_pending__",
|
|
||||||
role: "assistant",
|
|
||||||
text: "Working on it...",
|
|
||||||
thinking: ["Reading your request", "Planning the next actions", "Preparing the final answer"],
|
|
||||||
tools: [],
|
|
||||||
toolRuns: [],
|
|
||||||
createdAt: new Date().toISOString(),
|
|
||||||
pending: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
const activeChatConversation = computed<ChatConversation | null>(() => {
|
const activeChatConversation = computed<ChatConversation | null>(() => {
|
||||||
const activeId = authMe.value?.conversation.id;
|
const activeId = authMe.value?.conversation.id;
|
||||||
if (!activeId) return null;
|
if (!activeId) return null;
|
||||||
@@ -414,12 +396,17 @@ async function sendPilotMessage() {
|
|||||||
|
|
||||||
pilotSending.value = true;
|
pilotSending.value = true;
|
||||||
pilotInput.value = "";
|
pilotInput.value = "";
|
||||||
|
await loadPilotMessages().catch(() => {});
|
||||||
|
const pollId = setInterval(() => {
|
||||||
|
loadPilotMessages().catch(() => {});
|
||||||
|
}, 450);
|
||||||
try {
|
try {
|
||||||
await gqlFetch<{ sendPilotMessage: { ok: boolean } }>(sendPilotMessageMutation, { text });
|
await gqlFetch<{ sendPilotMessage: { ok: boolean } }>(sendPilotMessageMutation, { text });
|
||||||
await Promise.all([loadPilotMessages(), loadChatConversations()]);
|
await Promise.all([loadPilotMessages(), loadChatConversations()]);
|
||||||
} catch {
|
} catch {
|
||||||
pilotInput.value = text;
|
pilotInput.value = text;
|
||||||
} finally {
|
} finally {
|
||||||
|
clearInterval(pollId);
|
||||||
pilotSending.value = false;
|
pilotSending.value = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1258,13 +1245,12 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
|||||||
{{ thread.title }}
|
{{ thread.title }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<p v-if="chatThreadsLoading" class="mt-1 text-[11px] text-white/50">Loading threads...</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pilot-timeline min-h-0 flex-1 overflow-y-auto">
|
<div class="pilot-timeline min-h-0 flex-1 overflow-y-auto">
|
||||||
<div
|
<div
|
||||||
v-for="message in pilotTimeline"
|
v-for="message in pilotMessages"
|
||||||
:key="message.id"
|
:key="message.id"
|
||||||
class="pilot-row"
|
class="pilot-row"
|
||||||
>
|
>
|
||||||
@@ -1276,7 +1262,6 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
|||||||
<div class="pilot-meta">
|
<div class="pilot-meta">
|
||||||
<span class="pilot-author">{{ pilotRoleName(message.role) }}</span>
|
<span class="pilot-author">{{ pilotRoleName(message.role) }}</span>
|
||||||
<span class="pilot-time">{{ formatPilotStamp(message.createdAt) }}</span>
|
<span class="pilot-time">{{ formatPilotStamp(message.createdAt) }}</span>
|
||||||
<span v-if="message.pending" class="pilot-live-dot">Live</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pilot-message-text">
|
<div class="pilot-message-text">
|
||||||
@@ -1284,11 +1269,18 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="message.role !== 'user' && ((message.thinking && message.thinking.length) || (message.toolRuns && message.toolRuns.length) || (message.tools && message.tools.length))"
|
v-if="message.role !== 'user' && ((message.plan && message.plan.length) || (message.thinking && message.thinking.length) || (message.toolRuns && message.toolRuns.length) || (message.tools && message.tools.length))"
|
||||||
class="pilot-debug mt-2"
|
class="pilot-debug mt-2"
|
||||||
>
|
>
|
||||||
|
<div v-if="message.plan && message.plan.length" class="pilot-debug-block">
|
||||||
|
<p class="pilot-debug-title">Plan</p>
|
||||||
|
<ol class="pilot-debug-list">
|
||||||
|
<li v-for="(step, idx) in message.plan" :key="`plan-${message.id}-${idx}`">{{ step }}</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="message.thinking && message.thinking.length" class="pilot-debug-block">
|
<div v-if="message.thinking && message.thinking.length" class="pilot-debug-block">
|
||||||
<p class="pilot-debug-title">Thinking</p>
|
<p class="pilot-debug-title">Trace</p>
|
||||||
<ol class="pilot-debug-list">
|
<ol class="pilot-debug-list">
|
||||||
<li v-for="(step, idx) in message.thinking" :key="`thinking-${message.id}-${idx}`">{{ step }}</li>
|
<li v-for="(step, idx) in message.thinking" :key="`thinking-${message.id}-${idx}`">{{ step }}</li>
|
||||||
</ol>
|
</ol>
|
||||||
@@ -1317,6 +1309,7 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pilot-input-wrap">
|
<div class="pilot-input-wrap">
|
||||||
@@ -1328,7 +1321,7 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
|||||||
@keyup.enter="sendPilotMessage"
|
@keyup.enter="sendPilotMessage"
|
||||||
>
|
>
|
||||||
<button class="btn btn-sm border-0 bg-[#5865f2] text-white hover:bg-[#4752c4]" :disabled="pilotSending" @click="sendPilotMessage">
|
<button class="btn btn-sm border-0 bg-[#5865f2] text-white hover:bg-[#4752c4]" :disabled="pilotSending" @click="sendPilotMessage">
|
||||||
{{ pilotSending ? "..." : "Send" }}
|
{{ pilotSending ? "Sending..." : "Send" }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -2316,18 +2309,6 @@ async function decideFeedCard(card: FeedCard, decision: "accepted" | "rejected")
|
|||||||
color: rgba(255, 255, 255, 0.45);
|
color: rgba(255, 255, 255, 0.45);
|
||||||
}
|
}
|
||||||
|
|
||||||
.pilot-live-dot {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 10px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #c6ceff;
|
|
||||||
border-radius: 999px;
|
|
||||||
border: 1px solid rgba(124, 144, 255, 0.55);
|
|
||||||
padding: 1px 7px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pilot-message-text {
|
.pilot-message-text {
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
|||||||
@@ -360,30 +360,6 @@ async function main() {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
await prisma.chatMessage.createMany({
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
teamId: team.id,
|
|
||||||
conversationId: conversation.id,
|
|
||||||
authorUserId: null,
|
|
||||||
role: "ASSISTANT",
|
|
||||||
text: "Workspace is ready. I connected contacts, communications, calendar, deals, and recommendations.",
|
|
||||||
planJson: {
|
|
||||||
steps: ["Open any contact", "Review chat + pinned tasks", "Confirm next event or message"],
|
|
||||||
tools: ["contacts", "communications", "calendar", "feed"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
teamId: team.id,
|
|
||||||
conversationId: conversation.id,
|
|
||||||
authorUserId: null,
|
|
||||||
role: "ASSISTANT",
|
|
||||||
text: "Dataset loaded with 220 contacts and linked timeline activity.",
|
|
||||||
planJson: { steps: ["Filter by country/company", "Open active threads", "Apply one recommendation"], tools: ["search", "pins"] },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Seed completed.");
|
console.log("Seed completed.");
|
||||||
console.log(`Login phone: ${LOGIN_PHONE}`);
|
console.log(`Login phone: ${LOGIN_PHONE}`);
|
||||||
console.log(`Login password: ${LOGIN_PASSWORD}`);
|
console.log(`Login password: ${LOGIN_PASSWORD}`);
|
||||||
|
|||||||
@@ -30,6 +30,17 @@ export type AgentReply = {
|
|||||||
dbWrites?: Array<{ kind: string; detail: string }>;
|
dbWrites?: Array<{ kind: string; detail: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type AgentTraceEvent = {
|
||||||
|
text: string;
|
||||||
|
toolRun?: {
|
||||||
|
name: string;
|
||||||
|
status: "ok" | "error";
|
||||||
|
input: string;
|
||||||
|
output: string;
|
||||||
|
at: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
function normalize(s: string) {
|
function normalize(s: string) {
|
||||||
return s.trim().toLowerCase();
|
return s.trim().toLowerCase();
|
||||||
}
|
}
|
||||||
@@ -81,7 +92,12 @@ export async function runCrmAgent(userText: string): Promise<AgentReply> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function runCrmAgentFor(
|
export async function runCrmAgentFor(
|
||||||
input: { teamId: string; userId: string; userText: string },
|
input: {
|
||||||
|
teamId: string;
|
||||||
|
userId: string;
|
||||||
|
userText: string;
|
||||||
|
onTrace?: (event: AgentTraceEvent) => Promise<void> | void;
|
||||||
|
},
|
||||||
): Promise<AgentReply> {
|
): Promise<AgentReply> {
|
||||||
const mode = (process.env.CF_AGENT_MODE ?? "langgraph").toLowerCase();
|
const mode = (process.env.CF_AGENT_MODE ?? "langgraph").toLowerCase();
|
||||||
const llmApiKey =
|
const llmApiKey =
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
import type { AgentReply } from "./crmAgent";
|
import type { AgentReply, AgentTraceEvent } from "./crmAgent";
|
||||||
import { prisma } from "../utils/prisma";
|
import { prisma } from "../utils/prisma";
|
||||||
import { ensureDataset } from "../dataset/exporter";
|
import { ensureDataset } from "../dataset/exporter";
|
||||||
import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
import { createReactAgent } from "@langchain/langgraph/prebuilt";
|
||||||
@@ -322,6 +322,7 @@ export async function runLangGraphCrmAgentFor(input: {
|
|||||||
teamId: string;
|
teamId: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
userText: string;
|
userText: string;
|
||||||
|
onTrace?: (event: AgentTraceEvent) => Promise<void> | void;
|
||||||
}): Promise<AgentReply> {
|
}): Promise<AgentReply> {
|
||||||
const genericApiKey =
|
const genericApiKey =
|
||||||
process.env.LLM_API_KEY ||
|
process.env.LLM_API_KEY ||
|
||||||
@@ -388,6 +389,15 @@ export async function runLangGraphCrmAgentFor(input: {
|
|||||||
const toolRuns: NonNullable<AgentReply["toolRuns"]> = [];
|
const toolRuns: NonNullable<AgentReply["toolRuns"]> = [];
|
||||||
const pendingChanges: PendingChange[] = [];
|
const pendingChanges: PendingChange[] = [];
|
||||||
|
|
||||||
|
async function emitTrace(event: AgentTraceEvent) {
|
||||||
|
if (!input.onTrace) return;
|
||||||
|
try {
|
||||||
|
await input.onTrace(event);
|
||||||
|
} catch {
|
||||||
|
// Trace transport errors must not break the agent response.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function compact(value: unknown, max = 240) {
|
function compact(value: unknown, max = 240) {
|
||||||
const text = typeof value === "string" ? value : JSON.stringify(value);
|
const text = typeof value === "string" ? value : JSON.stringify(value);
|
||||||
if (!text) return "";
|
if (!text) return "";
|
||||||
@@ -510,6 +520,7 @@ export async function runLangGraphCrmAgentFor(input: {
|
|||||||
const toolName = `crm:${raw.action}`;
|
const toolName = `crm:${raw.action}`;
|
||||||
const startedAt = new Date().toISOString();
|
const startedAt = new Date().toISOString();
|
||||||
toolsUsed.push(toolName);
|
toolsUsed.push(toolName);
|
||||||
|
await emitTrace({ text: `Tool started: ${toolName}` });
|
||||||
|
|
||||||
const executeAction = async () => {
|
const executeAction = async () => {
|
||||||
if (raw.action === "get_snapshot") {
|
if (raw.action === "get_snapshot") {
|
||||||
@@ -753,21 +764,31 @@ export async function runLangGraphCrmAgentFor(input: {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await executeAction();
|
const result = await executeAction();
|
||||||
toolRuns.push({
|
const run = {
|
||||||
name: toolName,
|
name: toolName,
|
||||||
status: "ok",
|
status: "ok",
|
||||||
input: compact(raw),
|
input: compact(raw),
|
||||||
output: compact(result),
|
output: compact(result),
|
||||||
at: startedAt,
|
at: startedAt,
|
||||||
|
} as const;
|
||||||
|
toolRuns.push(run);
|
||||||
|
await emitTrace({
|
||||||
|
text: `Tool finished: ${toolName}`,
|
||||||
|
toolRun: run,
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
toolRuns.push({
|
const run = {
|
||||||
name: toolName,
|
name: toolName,
|
||||||
status: "error",
|
status: "error",
|
||||||
input: compact(raw),
|
input: compact(raw),
|
||||||
output: compact(error?.message || String(error)),
|
output: compact(error?.message || String(error)),
|
||||||
at: startedAt,
|
at: startedAt,
|
||||||
|
} as const;
|
||||||
|
toolRuns.push(run);
|
||||||
|
await emitTrace({
|
||||||
|
text: `Tool failed: ${toolName}`,
|
||||||
|
toolRun: run,
|
||||||
});
|
});
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@@ -796,7 +817,12 @@ export async function runLangGraphCrmAgentFor(input: {
|
|||||||
: {}),
|
: {}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const agent = createReactAgent({
|
const agent = useGigachat
|
||||||
|
? createReactAgent({
|
||||||
|
llm: model,
|
||||||
|
tools: [crmTool],
|
||||||
|
})
|
||||||
|
: createReactAgent({
|
||||||
llm: model,
|
llm: model,
|
||||||
tools: [crmTool],
|
tools: [crmTool],
|
||||||
responseFormat: z.object({
|
responseFormat: z.object({
|
||||||
@@ -831,13 +857,34 @@ export async function runLangGraphCrmAgentFor(input: {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const structured = res?.structuredResponse as { answer?: string; plan?: string[] } | undefined;
|
const structured = res?.structuredResponse as { answer?: string; plan?: string[] } | undefined;
|
||||||
const text = structured?.answer?.trim() || "Готово.";
|
const fallbackText = (() => {
|
||||||
const plan = Array.isArray(structured?.plan) ? structured!.plan : ["Собрать данные", "Сформировать ответ"];
|
const messages = Array.isArray(res?.messages) ? res.messages : [];
|
||||||
|
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
||||||
|
const msg = messages[i];
|
||||||
|
const type = String(msg?.type ?? "").toLowerCase();
|
||||||
|
if (type !== "ai") continue;
|
||||||
|
const content = msg?.content;
|
||||||
|
if (typeof content === "string" && content.trim()) return content.trim();
|
||||||
|
if (Array.isArray(content)) {
|
||||||
|
const text = content
|
||||||
|
.map((part: any) => (typeof part?.text === "string" ? part.text : ""))
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n")
|
||||||
|
.trim();
|
||||||
|
if (text) return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
})();
|
||||||
|
const text = structured?.answer?.trim() || fallbackText || "Готово.";
|
||||||
|
const plan = Array.isArray(structured?.plan) && structured.plan.length
|
||||||
|
? structured.plan
|
||||||
|
: ["Собрать данные", "Сформировать ответ"];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
text,
|
text,
|
||||||
plan,
|
plan,
|
||||||
thinking: plan,
|
thinking: [],
|
||||||
tools: toolsUsed,
|
tools: toolsUsed,
|
||||||
toolRuns,
|
toolRuns,
|
||||||
dbWrites: dbWrites.length ? dbWrites : undefined,
|
dbWrites: dbWrites.length ? dbWrites : undefined,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { clearAuthSession, setSession } from "../utils/auth";
|
|||||||
import { prisma } from "../utils/prisma";
|
import { prisma } from "../utils/prisma";
|
||||||
import { normalizePhone, verifyPassword } from "../utils/password";
|
import { normalizePhone, verifyPassword } from "../utils/password";
|
||||||
import { persistChatMessage, runCrmAgentFor } from "../agent/crmAgent";
|
import { persistChatMessage, runCrmAgentFor } from "../agent/crmAgent";
|
||||||
|
import type { AgentTraceEvent } from "../agent/crmAgent";
|
||||||
|
|
||||||
type GraphQLContext = {
|
type GraphQLContext = {
|
||||||
auth: AuthContext | null;
|
auth: AuthContext | null;
|
||||||
@@ -222,7 +223,7 @@ async function getChatMessages(auth: AuthContext | null) {
|
|||||||
role: m.role === "USER" ? "user" : m.role === "ASSISTANT" ? "assistant" : "system",
|
role: m.role === "USER" ? "user" : m.role === "ASSISTANT" ? "assistant" : "system",
|
||||||
text: m.text,
|
text: m.text,
|
||||||
plan: Array.isArray(debug.steps) ? (debug.steps as string[]) : [],
|
plan: Array.isArray(debug.steps) ? (debug.steps as string[]) : [],
|
||||||
thinking: Array.isArray(debug.thinking) ? (debug.thinking as string[]) : Array.isArray(debug.steps) ? (debug.steps as string[]) : [],
|
thinking: Array.isArray(debug.thinking) ? (debug.thinking as string[]) : [],
|
||||||
tools: Array.isArray(debug.tools) ? (debug.tools as string[]) : [],
|
tools: Array.isArray(debug.tools) ? (debug.tools as string[]) : [],
|
||||||
toolRuns: Array.isArray(debug.toolRuns)
|
toolRuns: Array.isArray(debug.toolRuns)
|
||||||
? (debug.toolRuns as any[])
|
? (debug.toolRuns as any[])
|
||||||
@@ -510,7 +511,24 @@ async function sendPilotMessage(auth: AuthContext | null, textInput: string) {
|
|||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
|
|
||||||
const reply = await runCrmAgentFor({ teamId: ctx.teamId, userId: ctx.userId, userText: text });
|
const reply = await runCrmAgentFor({
|
||||||
|
teamId: ctx.teamId,
|
||||||
|
userId: ctx.userId,
|
||||||
|
userText: text,
|
||||||
|
onTrace: async (event: AgentTraceEvent) => {
|
||||||
|
await persistChatMessage({
|
||||||
|
teamId: ctx.teamId,
|
||||||
|
conversationId: ctx.conversationId,
|
||||||
|
authorUserId: null,
|
||||||
|
role: "SYSTEM",
|
||||||
|
text: event.text,
|
||||||
|
plan: [],
|
||||||
|
thinking: [],
|
||||||
|
tools: event.toolRun ? [event.toolRun.name] : [],
|
||||||
|
toolRuns: event.toolRun ? [event.toolRun] : [],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
await persistChatMessage({
|
await persistChatMessage({
|
||||||
teamId: ctx.teamId,
|
teamId: ctx.teamId,
|
||||||
conversationId: ctx.conversationId,
|
conversationId: ctx.conversationId,
|
||||||
@@ -518,7 +536,7 @@ async function sendPilotMessage(auth: AuthContext | null, textInput: string) {
|
|||||||
role: "ASSISTANT",
|
role: "ASSISTANT",
|
||||||
text: reply.text,
|
text: reply.text,
|
||||||
plan: reply.plan,
|
plan: reply.plan,
|
||||||
thinking: reply.thinking ?? reply.plan,
|
thinking: reply.thinking ?? [],
|
||||||
tools: reply.tools,
|
tools: reply.tools,
|
||||||
toolRuns: reply.toolRuns ?? [],
|
toolRuns: reply.toolRuns ?? [],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ services:
|
|||||||
REDIS_URL: "redis://redis:6379"
|
REDIS_URL: "redis://redis:6379"
|
||||||
CF_AGENT_MODE: "langgraph"
|
CF_AGENT_MODE: "langgraph"
|
||||||
OPENAI_MODEL: "gpt-4o-mini"
|
OPENAI_MODEL: "gpt-4o-mini"
|
||||||
GIGACHAT_AUTH_KEY: "${GIGACHAT_AUTH_KEY:-}"
|
GIGACHAT_AUTH_KEY: "MDE5YzQwNmQtMDM0NC03MTVlLTg4MTAtOWZlYjlmNzQwY2E3OmNhZTg5NmM1LWZiOGEtNGZkZS04ODA0LWZkYjYyYzVlMTI0OQ=="
|
||||||
GIGACHAT_MODEL: "${GIGACHAT_MODEL:-GigaChat-2-Max}"
|
GIGACHAT_MODEL: "GigaChat-2"
|
||||||
GIGACHAT_SCOPE: "${GIGACHAT_SCOPE:-GIGACHAT_API_PERS}"
|
GIGACHAT_SCOPE: "GIGACHAT_API_PERS"
|
||||||
|
NODE_TLS_REJECT_UNAUTHORIZED: "0"
|
||||||
# Set this in your shell or a compose override:
|
# Set this in your shell or a compose override:
|
||||||
# OPENAI_API_KEY: "..."
|
# OPENAI_API_KEY: "..."
|
||||||
command: >
|
command: >
|
||||||
|
|||||||
Reference in New Issue
Block a user