optimize code
This commit is contained in:
@@ -14,7 +14,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
reader: null,
|
||||
};
|
||||
|
||||
marked.setOptions({ breaks: true });
|
||||
marked.setOptions({breaks: true});
|
||||
|
||||
function renderMarkdown(text) {
|
||||
return DOMPurify.sanitize(marked.parse(text));
|
||||
@@ -365,8 +365,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
try {
|
||||
const res = await fetch('/ask-sse', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ prompt }),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
prompt,
|
||||
fullContext: false,
|
||||
}),
|
||||
signal: state.abortController.signal,
|
||||
});
|
||||
|
||||
@@ -382,13 +385,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
while (!state.abortRequested) {
|
||||
const { value, done } = await state.reader.read();
|
||||
const {value, done} = await state.reader.read();
|
||||
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
|
||||
sseBuffer += decoder.decode(value, { stream: true });
|
||||
sseBuffer += decoder.decode(value, {stream: true});
|
||||
|
||||
const parsed = parseSseEvents(sseBuffer);
|
||||
sseBuffer = parsed.rest;
|
||||
@@ -398,7 +401,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { eventName, data } = readSseEvent(rawEvent);
|
||||
const {eventName, data} = readSseEvent(rawEvent);
|
||||
|
||||
if (!data && eventName !== 'done') {
|
||||
continue;
|
||||
@@ -480,7 +483,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
|
||||
try {
|
||||
await fetch('/history/delete', { method: 'POST' });
|
||||
await fetch('/history/delete', {method: 'POST'});
|
||||
} catch (err) {
|
||||
console.error('History delete failed:', err);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ final readonly class AgentRunner
|
||||
$this->systemMsgOn = true;
|
||||
}
|
||||
|
||||
public function run(string $prompt, string $userId, bool $includeFullContext = false): Generator
|
||||
public function run(string $prompt, string $userId, bool $forceFullContext = false): Generator
|
||||
{
|
||||
$prompt = trim($prompt);
|
||||
|
||||
@@ -66,7 +66,7 @@ final readonly class AgentRunner
|
||||
]);
|
||||
|
||||
try {
|
||||
if ($includeFullContext) {
|
||||
if ($forceFullContext) {
|
||||
// Full context mode is already passed to PromptBuilder.
|
||||
// Additional context strategies can be added here later.
|
||||
}
|
||||
@@ -164,7 +164,7 @@ final readonly class AgentRunner
|
||||
urlContent: $urlContent,
|
||||
knowledgeChunks: $knowledgeChunks,
|
||||
shopResults: $shopResults,
|
||||
fullContext: $includeFullContext,
|
||||
fullContext: $forceFullContext,
|
||||
swagFullOutPut: $optimizedShopQuery
|
||||
);
|
||||
|
||||
@@ -187,7 +187,7 @@ final readonly class AgentRunner
|
||||
'userId' => $userId,
|
||||
'context' => $this->contextService->buildUserContext(
|
||||
$userId,
|
||||
$includeFullContext
|
||||
$forceFullContext
|
||||
),
|
||||
]);
|
||||
}
|
||||
@@ -217,7 +217,7 @@ final readonly class AgentRunner
|
||||
$this->agentLogger->info('Agent run finished', [
|
||||
'userId' => $userId,
|
||||
'outputLength' => mb_strlen($fullOutput),
|
||||
'contextMode' => $includeFullContext ? 'full' : 'recent',
|
||||
'contextMode' => $forceFullContext ? 'full' : 'recent',
|
||||
'commerceIntent' => $commerceIntent,
|
||||
'primaryShopResultsCount' => count($primaryShopResults),
|
||||
'shopResultsCount' => count($shopResults),
|
||||
|
||||
@@ -14,39 +14,40 @@ use Symfony\Component\Routing\Annotation\Route;
|
||||
final readonly class AskSseController
|
||||
{
|
||||
public function __construct(
|
||||
private AgentRunner $agentRunner,
|
||||
private AgentRunner $agentRunner,
|
||||
private ClientIdResolver $clientIdResolver,
|
||||
) {}
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route('/ask-sse', name: 'ask_sse', methods: ['POST'])]
|
||||
public function stream(Request $request): StreamedResponse
|
||||
{
|
||||
$data = json_decode($request->getContent(), true);
|
||||
$data = json_decode($request->getContent(), true);
|
||||
$prompt = trim((string) ($data['prompt'] ?? ''));
|
||||
|
||||
/**
|
||||
* Default:
|
||||
* - Browser chat uses budgeted recent context
|
||||
* - Full context must be explicitly requested
|
||||
*/
|
||||
$includeFullContext = filter_var(
|
||||
$data['fullContext'] ?? false,
|
||||
FILTER_VALIDATE_BOOL
|
||||
);
|
||||
|
||||
$cookieResponse = new Response();
|
||||
$clientId = $this->clientIdResolver->resolve($request, $cookieResponse);
|
||||
|
||||
return new StreamedResponse(
|
||||
function () use ($prompt, $clientId, $cookieResponse): void {
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Disable all PHP output buffering
|
||||
// ---------------------------------------------------------
|
||||
function () use ($prompt, $clientId, $cookieResponse, $includeFullContext): void {
|
||||
while (ob_get_level() > 0) {
|
||||
ob_end_flush();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Forward cookies
|
||||
// ---------------------------------------------------------
|
||||
foreach ($cookieResponse->headers->getCookies() as $cookie) {
|
||||
header('Set-Cookie: ' . $cookie, false);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// SSE prelude
|
||||
// ---------------------------------------------------------
|
||||
echo "retry: 3000\n\n";
|
||||
flush();
|
||||
|
||||
@@ -56,11 +57,8 @@ final readonly class AskSseController
|
||||
return;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// 🔥 FIXED: Sende Chunks direkt (behält \n!)
|
||||
// ---------------------------------------------------------
|
||||
try {
|
||||
foreach ($this->agentRunner->run($prompt, $clientId, true) as $chunk) {
|
||||
foreach ($this->agentRunner->run($prompt, $clientId, $includeFullContext) as $chunk) {
|
||||
$chunk = str_replace(["\r\n", "\r"], "\n", $chunk);
|
||||
$this->sendData($chunk);
|
||||
}
|
||||
@@ -71,44 +69,30 @@ final readonly class AskSseController
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Signal completion
|
||||
// ---------------------------------------------------------
|
||||
$this->sendEvent('done', '[DONE]');
|
||||
},
|
||||
200,
|
||||
[
|
||||
'Content-Type' => 'text/event-stream; charset=utf-8',
|
||||
'Cache-Control' => 'no-cache, no-store, must-revalidate',
|
||||
'Connection' => 'keep-alive',
|
||||
'Content-Type' => 'text/event-stream; charset=utf-8',
|
||||
'Cache-Control' => 'no-cache, no-store, must-revalidate',
|
||||
'Connection' => 'keep-alive',
|
||||
'X-Accel-Buffering' => 'no',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXED: Behält Markdown-Struktur (\n) bei
|
||||
*
|
||||
* SSE erlaubt mehrere "data:"-Zeilen pro Event.
|
||||
* Jede Zeile wird als separate data-Zeile gesendet.
|
||||
*/
|
||||
private function sendData(string $data): void
|
||||
{
|
||||
// Split by \n und sende jede Zeile einzeln
|
||||
$lines = explode("\n", $data);
|
||||
|
||||
foreach ($lines as $line) {
|
||||
echo 'data: ' . $line . "\n";
|
||||
}
|
||||
|
||||
// Leere Zeile = Ende der SSE-Message
|
||||
echo "\n\n";
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a named SSE event.
|
||||
*/
|
||||
private function sendEvent(string $event, string $data): void
|
||||
{
|
||||
$safe = str_replace(["\r", "\n"], ' ', $data);
|
||||
|
||||
Reference in New Issue
Block a user