added jenkins
This commit is contained in:
Vendored
+2
-2
@@ -10,7 +10,7 @@
|
||||
|
||||
pipeline {
|
||||
agent {
|
||||
label 'Мастер'
|
||||
label 'мастер'
|
||||
}
|
||||
|
||||
options {
|
||||
@@ -33,7 +33,7 @@ pipeline {
|
||||
)
|
||||
string(
|
||||
name: 'BACKEND_HEALTH_URL',
|
||||
defaultValue: 'http://127.0.0.1:8080/api/v1/health',
|
||||
defaultValue: 'http://127.0.0.1:8202/api/v1/health',
|
||||
description: 'Проверка после деплоя'
|
||||
)
|
||||
booleanParam(
|
||||
|
||||
@@ -292,10 +292,14 @@ class ChatService:
|
||||
reasoning_details: list[Any] | None = None
|
||||
finish_reason = ""
|
||||
|
||||
# После tool-раунда стримим вживую; до tools — буфер (иначе текст «переписывает» notice).
|
||||
stream_live = tools_executed > 0
|
||||
|
||||
async for event in self.llm.stream_chat(messages, tools=TOOL_DEFINITIONS):
|
||||
if event["type"] == "content":
|
||||
content_parts.append(event["content"])
|
||||
yield self._sse("token", {"content": event["content"]})
|
||||
if stream_live:
|
||||
yield self._sse("token", {"content": event["content"]})
|
||||
elif event["type"] == "reasoning":
|
||||
reasoning = event.get("reasoning", "") or reasoning
|
||||
if event.get("reasoning_details"):
|
||||
@@ -390,8 +394,12 @@ class ChatService:
|
||||
|
||||
continue
|
||||
|
||||
if content_parts and not stream_live:
|
||||
for part in content_parts:
|
||||
yield self._sse("token", {"content": part})
|
||||
|
||||
final_content = "".join(content_parts).strip()
|
||||
if not final_content and streamed_reply_parts:
|
||||
if not final_content and streamed_reply_parts and tools_executed == 0:
|
||||
final_content = "".join(streamed_reply_parts).strip()
|
||||
if not final_content and reasoning:
|
||||
final_content = reasoning.strip()
|
||||
|
||||
+38
-24
@@ -48,8 +48,8 @@ export default function Chat() {
|
||||
const [pendingPhase, setPendingPhase] = useState<
|
||||
"thinking" | "preparing" | "generating" | "tools"
|
||||
>("thinking");
|
||||
const [liveNotices, setLiveNotices] = useState<string[]>([]);
|
||||
const [chatError, setChatError] = useState<string | null>(null);
|
||||
const tempMessageId = useRef(0);
|
||||
const messagesRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
const scrollRafRef = useRef<number | null>(null);
|
||||
@@ -104,23 +104,38 @@ export default function Chat() {
|
||||
cancelAnimationFrame(scrollRafRef.current);
|
||||
}
|
||||
};
|
||||
}, [messages, streaming, liveNotices, loading, chatError, scrollToBottom]);
|
||||
}, [messages, streaming, loading, chatError, scrollToBottom]);
|
||||
|
||||
const dismissKeyboard = useCallback(() => {
|
||||
inputRef.current?.blur();
|
||||
}, []);
|
||||
|
||||
const waitingForStream = loading && !streaming;
|
||||
const nextTempId = () => {
|
||||
tempMessageId.current -= 1;
|
||||
return tempMessageId.current;
|
||||
};
|
||||
|
||||
const appendNotice = useCallback((content: string) => {
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: nextTempId(),
|
||||
role: "notice",
|
||||
content,
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
]);
|
||||
}, []);
|
||||
|
||||
const pendingLabel =
|
||||
pendingPhase === "tools"
|
||||
? "Выполняю команды…"
|
||||
: liveNotices.length > 0
|
||||
? "Обрабатываю…"
|
||||
: pendingPhase === "preparing"
|
||||
? "Собираю контекст…"
|
||||
: pendingPhase === "generating"
|
||||
? "Генерирую ответ…"
|
||||
: "Думаю…";
|
||||
: pendingPhase === "preparing"
|
||||
? "Собираю контекст…"
|
||||
: pendingPhase === "generating"
|
||||
? "Генерирую ответ…"
|
||||
: "Думаю…";
|
||||
|
||||
useEffect(() => {
|
||||
const seq = pomodoroStatus?.cycle?.chat_notify_seq ?? 0;
|
||||
@@ -177,7 +192,6 @@ export default function Chat() {
|
||||
await loadSessions();
|
||||
setActiveId(session.id);
|
||||
setMessages([]);
|
||||
setLiveNotices([]);
|
||||
};
|
||||
|
||||
const handleDelete = async (id: number) => {
|
||||
@@ -187,7 +201,6 @@ export default function Chat() {
|
||||
if (activeId === id) {
|
||||
setActiveId(data[0]?.id ?? null);
|
||||
setMessages([]);
|
||||
setLiveNotices([]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -201,11 +214,10 @@ export default function Chat() {
|
||||
setLoading(true);
|
||||
setStreaming("");
|
||||
setPendingPhase("thinking");
|
||||
setLiveNotices([]);
|
||||
setChatError(null);
|
||||
|
||||
const tempUser: ChatMessage = {
|
||||
id: Date.now(),
|
||||
id: nextTempId(),
|
||||
role: "user",
|
||||
content: text,
|
||||
created_at: new Date().toISOString(),
|
||||
@@ -234,7 +246,7 @@ export default function Chat() {
|
||||
setStreaming(assistantText);
|
||||
}
|
||||
if (chunk.event === "notice") {
|
||||
setLiveNotices((prev) => [...prev, chunk.data.content]);
|
||||
appendNotice(chunk.data.content);
|
||||
if (String(chunk.data.content).startsWith("⏱")) {
|
||||
refreshPomodoro();
|
||||
}
|
||||
@@ -243,8 +255,19 @@ export default function Chat() {
|
||||
refreshPomodoro();
|
||||
}
|
||||
if (chunk.event === "done") {
|
||||
const tail = assistantText.trim();
|
||||
if (tail) {
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: nextTempId(),
|
||||
role: "assistant",
|
||||
content: tail,
|
||||
created_at: new Date().toISOString(),
|
||||
},
|
||||
]);
|
||||
}
|
||||
setStreaming("");
|
||||
setLiveNotices([]);
|
||||
setChatError(null);
|
||||
await loadMessages(activeId);
|
||||
await loadSessions();
|
||||
@@ -334,15 +357,6 @@ export default function Chat() {
|
||||
</div>
|
||||
))}
|
||||
|
||||
{liveNotices.map((notice, idx) => (
|
||||
<div key={`notice-${idx}`} className="message message-notice">
|
||||
<div className="message-role">{noticeLabel(notice)}</div>
|
||||
<div className="message-content">
|
||||
<ReactMarkdown>{notice}</ReactMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{waitingForStream && (
|
||||
<div className="message message-assistant message-pending" aria-live="polite">
|
||||
<div className="message-role">assistant</div>
|
||||
|
||||
Reference in New Issue
Block a user