refactor: extract shared SSE parser and add eslint to iknowyou
Deduplicate SSE chunk parsing from input-bar.tsx and vllm.ts into a shared lib/sse.ts consumeSSEChunk helper. Add eslint + next lint config. Silence monorepo lockfile warning via outputFileTracingRoot. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
eca7765e8d
commit
51ac377376
3
applications/iknowyou/.eslintrc.json
Normal file
3
applications/iknowyou/.eslintrc.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"extends": "next/core-web-vitals"
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useRef, useCallback } from "react";
|
import { useRef, useCallback } from "react";
|
||||||
import { useChatStore } from "@/lib/store";
|
import { useChatStore } from "@/lib/store";
|
||||||
|
import { consumeSSEChunk } from "@/lib/sse";
|
||||||
|
|
||||||
export function InputBar() {
|
export function InputBar() {
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
@ -58,17 +59,15 @@ export function InputBar() {
|
|||||||
const { done, value } = await reader.read();
|
const { done, value } = await reader.read();
|
||||||
if (done) break;
|
if (done) break;
|
||||||
|
|
||||||
buffer += decoder.decode(value, { stream: true });
|
const { jsonLines, buffer: next } = consumeSSEChunk(
|
||||||
const lines = buffer.split("\n");
|
buffer,
|
||||||
buffer = lines.pop()!;
|
decoder.decode(value, { stream: true })
|
||||||
|
);
|
||||||
for (const line of lines) {
|
buffer = next;
|
||||||
const trimmed = line.trim();
|
|
||||||
if (!trimmed.startsWith("data: ") || trimmed === "data: [DONE]")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
|
for (const jsonStr of jsonLines) {
|
||||||
try {
|
try {
|
||||||
const data = JSON.parse(trimmed.slice(6));
|
const data = JSON.parse(jsonStr);
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
setError(data.error);
|
setError(data.error);
|
||||||
return;
|
return;
|
||||||
|
|||||||
27
applications/iknowyou/lib/sse.ts
Normal file
27
applications/iknowyou/lib/sse.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Append a decoded SSE chunk to the running buffer and extract complete data lines.
|
||||||
|
*
|
||||||
|
* Returns the raw JSON strings from each `data: {...}` line and the updated
|
||||||
|
* buffer (partial-line remainder). Skips the `[DONE]` sentinel.
|
||||||
|
*
|
||||||
|
* Both the vLLM server-side reader and the client-side chat reader share
|
||||||
|
* this logic — keeping buffer management in one place prevents drift when
|
||||||
|
* the SSE wire format changes.
|
||||||
|
*/
|
||||||
|
export function consumeSSEChunk(
|
||||||
|
buffer: string,
|
||||||
|
chunk: string
|
||||||
|
): { jsonLines: string[]; buffer: string } {
|
||||||
|
const full = buffer + chunk;
|
||||||
|
const parts = full.split("\n");
|
||||||
|
const newBuffer = parts.pop()!;
|
||||||
|
const jsonLines: string[] = [];
|
||||||
|
|
||||||
|
for (const part of parts) {
|
||||||
|
const trimmed = part.trim();
|
||||||
|
if (!trimmed.startsWith("data: ") || trimmed === "data: [DONE]") continue;
|
||||||
|
jsonLines.push(trimmed.slice(6));
|
||||||
|
}
|
||||||
|
|
||||||
|
return { jsonLines, buffer: newBuffer };
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import type { CommunicationBrief } from "./types";
|
import type { CommunicationBrief } from "./types";
|
||||||
|
import { consumeSSEChunk } from "./sse";
|
||||||
|
|
||||||
const VLLM_BASE = process.env.VLLM_URL ?? "http://msd5685.mjhst.com:8000";
|
const VLLM_BASE = process.env.VLLM_URL ?? "http://msd5685.mjhst.com:8000";
|
||||||
const MODEL = "Qwen/Qwen3-8B";
|
const MODEL = "Qwen/Qwen3-8B";
|
||||||
@ -147,17 +148,15 @@ export async function* streamChat(
|
|||||||
const { done, value } = await reader.read();
|
const { done, value } = await reader.read();
|
||||||
if (done) break;
|
if (done) break;
|
||||||
|
|
||||||
buffer += decoder.decode(value, { stream: true });
|
const { jsonLines, buffer: next } = consumeSSEChunk(
|
||||||
const lines = buffer.split("\n");
|
buffer,
|
||||||
buffer = lines.pop()!;
|
decoder.decode(value, { stream: true })
|
||||||
|
);
|
||||||
for (const line of lines) {
|
buffer = next;
|
||||||
const trimmed = line.trim();
|
|
||||||
if (!trimmed.startsWith("data: ") || trimmed === "data: [DONE]")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
|
for (const jsonStr of jsonLines) {
|
||||||
try {
|
try {
|
||||||
const chunk = JSON.parse(trimmed.slice(6));
|
const chunk = JSON.parse(jsonStr);
|
||||||
const token = chunk.choices?.[0]?.delta?.content;
|
const token = chunk.choices?.[0]?.delta?.content;
|
||||||
if (token) yield token;
|
if (token) yield token;
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {};
|
const nextConfig: NextConfig = {
|
||||||
|
// Silence the "multiple lockfiles" warning — this project lives inside a
|
||||||
|
// larger monorepo and has its own lockfile by design.
|
||||||
|
outputFileTracingRoot: path.join(__dirname, "../../"),
|
||||||
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|||||||
4523
applications/iknowyou/package-lock.json
generated
4523
applications/iknowyou/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,8 @@
|
|||||||
"dev:engine": "cargo run -p iknowyou-engine --bin server --features synap-aux",
|
"dev:engine": "cargo run -p iknowyou-engine --bin server --features synap-aux",
|
||||||
"dev:all": "sh -c 'npm run dev:engine & npm run dev'",
|
"dev:all": "sh -c 'npm run dev:engine & npm run dev'",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start -p 59521"
|
"start": "next start -p 59521",
|
||||||
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "^15.3.3",
|
"next": "^15.3.3",
|
||||||
@ -20,6 +21,8 @@
|
|||||||
"@types/node": "^22.15.21",
|
"@types/node": "^22.15.21",
|
||||||
"@types/react": "^19.1.4",
|
"@types/react": "^19.1.4",
|
||||||
"@types/react-dom": "^19.1.5",
|
"@types/react-dom": "^19.1.5",
|
||||||
|
"eslint": "^8.57.1",
|
||||||
|
"eslint-config-next": "^15.5.12",
|
||||||
"tailwindcss": "^4.1.8",
|
"tailwindcss": "^4.1.8",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user