@laintern/chat-sdk 0.1.0
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/README.md +121 -0
- package/dist/chunk-WMKNNVMS.js +266 -0
- package/dist/chunk-WMKNNVMS.js.map +1 -0
- package/dist/conversation-7VzVDT95.d.cts +284 -0
- package/dist/conversation-7VzVDT95.d.ts +284 -0
- package/dist/index.cjs +538 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +43 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +270 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.cjs +268 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +25 -0
- package/dist/react/index.d.ts +25 -0
- package/dist/react/index.js +30 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +67 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/errors.ts","../../src/conversation.ts","../../src/react/index.ts"],"names":["useRef","useMemo","useSyncExternalStore","useCallback"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Every failure the SDK surfaces, with the API's own error code attached.\n *\n * Callers switch on `code` rather than parsing messages: the server's copy is\n * user-facing prose that may change, while the code is part of the contract.\n */\nexport class LainternError extends Error {\n readonly code: string\n readonly status: number | null\n /** Set on RATE_LIMIT_EXCEEDED. */\n readonly rateLimitScope?: \"user\" | \"agent\"\n readonly rateLimitWindow?: \"minute\" | \"hour\" | \"day\"\n /** Set on MCP_UNAVAILABLE: which dependency failed, and how. */\n readonly reason?: string\n\n constructor(\n code: string,\n message: string,\n options: {\n status?: number | null\n rateLimitScope?: \"user\" | \"agent\"\n rateLimitWindow?: \"minute\" | \"hour\" | \"day\"\n reason?: string\n cause?: unknown\n } = {},\n ) {\n super(message, options.cause !== undefined ? { cause: options.cause } : undefined)\n this.name = \"LainternError\"\n this.code = code\n this.status = options.status ?? null\n if (options.rateLimitScope) this.rateLimitScope = options.rateLimitScope\n if (options.rateLimitWindow) this.rateLimitWindow = options.rateLimitWindow\n if (options.reason) this.reason = options.reason\n }\n\n /**\n * True when retrying later could plausibly succeed: a rate limit, or a\n * dependency the server could not reach. An expired session is NOT retryable\n * — the host has to mint a new token first.\n */\n get isRetryable(): boolean {\n return this.code === \"RATE_LIMIT_EXCEEDED\" || this.reason === \"transport\"\n }\n}\n\nfunction pick<T extends string>(value: unknown, allowed: readonly T[]): T | undefined {\n return typeof value === \"string\" && (allowed as readonly string[]).includes(value)\n ? (value as T)\n : undefined\n}\n\n/** Turn a non-2xx API response into a `LainternError`. Never throws itself. */\nexport async function errorFromResponse(response: Response): Promise<LainternError> {\n let code = `HTTP_${response.status}`\n let message = \"An unexpected error occurred.\"\n let rateLimitScope: \"user\" | \"agent\" | undefined\n let rateLimitWindow: \"minute\" | \"hour\" | \"day\" | undefined\n let reason: string | undefined\n\n try {\n const body = (await response.json()) as {\n error?: {\n code?: string\n message?: string\n scope?: unknown\n window?: unknown\n reason?: unknown\n }\n message?: string\n }\n if (body?.error?.code) {\n code = body.error.code\n message = body.error.message ?? message\n rateLimitScope = pick(body.error.scope, [\"user\", \"agent\"] as const)\n rateLimitWindow = pick(body.error.window, [\"minute\", \"hour\", \"day\"] as const)\n if (typeof body.error.reason === \"string\") reason = body.error.reason\n } else if (typeof body?.message === \"string\") {\n message = body.message\n }\n } catch {\n // Not JSON — keep the HTTP-derived defaults.\n }\n\n return new LainternError(code, message, {\n status: response.status,\n rateLimitScope,\n rateLimitWindow,\n reason,\n })\n}\n","import type {\n CitationSource,\n ExternalSourceType,\n SearchResult,\n StepEvent,\n} from \"@laintern-headless/types\"\nimport type { LainternClient } from \"./client.js\"\nimport { LainternError } from \"./errors.js\"\n\n/** Soft advisory alongside an answer — the agent flagged the topic, but answered. */\nexport interface TurnAdvisory {\n redirect: string | null\n message: string\n}\n\n/** A guardrail refused the turn. The agent produced no answer. */\nexport interface TurnBlock {\n /** Which guardrail fired, when the server names one. */\n slug: string | null\n /** Copy meant for the end user, written by the guardrail's configuration. */\n message: string\n}\n\nexport interface TurnError {\n code: string\n message: string\n rateLimitScope?: \"user\" | \"agent\"\n rateLimitWindow?: \"minute\" | \"hour\" | \"day\"\n}\n\nexport interface Turn {\n id: string\n userMessage: string\n userImages?: string[]\n assistantContent: string\n /** Citation number (the `[1]` in the text) → the source it points at. */\n citations: Map<number, CitationSource>\n /** Everything the agent cited or consulted, in the order the server ranked it. */\n sources: SearchResult[]\n /** Sidebar suggestions per content type — not necessarily cited. */\n externalSources: Partial<Record<ExternalSourceType, SearchResult[]>>\n mode: string | null\n steps: StepEvent[]\n isStreaming: boolean\n error?: TurnError\n blocked?: TurnBlock\n advisory?: TurnAdvisory\n messageId?: string\n feedback?: \"positive\" | \"negative\" | null\n}\n\nexport interface ConversationState {\n turns: Turn[]\n sessionId: string | null\n isStreaming: boolean\n}\n\nexport interface SendOptions {\n images?: string[]\n forceTool?: string\n}\n\nexport interface Conversation {\n getState(): ConversationState\n /** Subscribe to state changes. Returns an unsubscribe function. */\n subscribe(listener: (state: ConversationState) => void): () => void\n /** Send a message and stream the answer into state. Never throws. */\n send(message: string, options?: SendOptions): Promise<void>\n /** Stop the current stream. The partial answer stays in state. */\n abort(): void\n /** Drop all turns and start a new server-side session on the next send. */\n reset(): void\n setFeedback(turnId: string, feedback: \"positive\" | \"negative\"): Promise<void>\n}\n\nexport interface ConversationOptions {\n /** Resume an existing server-side session. */\n sessionId?: string | null\n /** Restore turns from your own storage. */\n initialTurns?: Turn[]\n /** Called whenever the session id changes, so you can persist it. */\n onSessionId?: (sessionId: string) => void\n}\n\nfunction newId(): string {\n const c = globalThis.crypto\n if (c && typeof c.randomUUID === \"function\") return c.randomUUID()\n return `turn-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`\n}\n\nfunction createTurn(message: string, images?: string[]): Turn {\n return {\n id: newId(),\n userMessage: message,\n ...(images?.length ? { userImages: images } : {}),\n assistantContent: \"\",\n citations: new Map(),\n sources: [],\n externalSources: {},\n mode: null,\n steps: [],\n isStreaming: true,\n }\n}\n\n/**\n * Stateful conversation on top of {@link LainternClient}.\n *\n * Holds the turn list, applies each stream event to the turn in flight, and\n * notifies subscribers. Framework-agnostic on purpose: the React binding in\n * `@laintern/chat-sdk/react` is a thin `useSyncExternalStore` wrapper, and any\n * other UI layer can subscribe the same way.\n *\n * `send()` never throws. A failed turn is part of the conversation — it lands\n * on `turn.error` (or `turn.blocked` for a guardrail) so the UI can render it\n * in place, which is what you want in a chat transcript.\n */\nexport function createConversation(\n client: LainternClient,\n options: ConversationOptions = {},\n): Conversation {\n let state: ConversationState = {\n turns: options.initialTurns ?? [],\n sessionId: options.sessionId ?? null,\n isStreaming: false,\n }\n const listeners = new Set<(state: ConversationState) => void>()\n let controller: AbortController | null = null\n\n function emit(next: ConversationState): void {\n state = next\n for (const listener of listeners) listener(state)\n }\n\n /** Apply a change to the turn currently streaming (always the last one). */\n function patchLast(patch: (turn: Turn) => Turn): void {\n const lastIndex = state.turns.length - 1\n if (lastIndex < 0) return\n const turns = [...state.turns]\n turns[lastIndex] = patch(turns[lastIndex])\n emit({ ...state, turns })\n }\n\n return {\n getState: () => state,\n\n subscribe(listener) {\n listeners.add(listener)\n return () => listeners.delete(listener)\n },\n\n abort() {\n controller?.abort()\n },\n\n reset() {\n controller?.abort()\n emit({ turns: [], sessionId: null, isStreaming: false })\n },\n\n async send(message, sendOptions = {}) {\n controller?.abort()\n controller = new AbortController()\n const signal = controller.signal\n\n emit({\n ...state,\n turns: [...state.turns, createTurn(message, sendOptions.images)],\n isStreaming: true,\n })\n\n try {\n const stream = client.sendMessage(message, {\n sessionId: state.sessionId,\n ...(sendOptions.images ? { images: sendOptions.images } : {}),\n ...(sendOptions.forceTool ? { forceTool: sendOptions.forceTool } : {}),\n signal,\n })\n\n for await (const event of stream) {\n if (signal.aborted) break\n\n switch (event.type) {\n case \"session\":\n emit({ ...state, sessionId: event.session_id })\n options.onSessionId?.(event.session_id)\n break\n case \"mode\":\n patchLast((turn) => ({ ...turn, mode: event.mode }))\n break\n case \"step\":\n patchLast((turn) => ({ ...turn, steps: mergeStep(turn.steps, event.step) }))\n break\n case \"delta\":\n patchLast((turn) => ({\n ...turn,\n assistantContent: turn.assistantContent + event.content,\n }))\n break\n case \"citation\":\n patchLast((turn) => {\n const citations = new Map(turn.citations)\n citations.set(event.index, event.source)\n return { ...turn, citations }\n })\n break\n case \"sources\":\n patchLast((turn) => ({ ...turn, sources: event.items }))\n break\n case \"external_sources\":\n patchLast((turn) => ({\n ...turn,\n externalSources: {\n ...turn.externalSources,\n [event.source_type]: event.items,\n },\n }))\n break\n case \"advisory\":\n patchLast((turn) => ({\n ...turn,\n advisory: { redirect: event.redirect ?? null, message: event.message },\n }))\n break\n case \"done\":\n patchLast((turn) => ({\n ...turn,\n isStreaming: false,\n ...(event.message_id ? { messageId: event.message_id } : {}),\n }))\n emit({ ...state, isStreaming: false })\n break\n case \"error\":\n applyFailure(patchLast, {\n code: event.code,\n message: event.message,\n guardrailSlug: event.guardrail_slug ?? null,\n })\n break\n }\n }\n } catch (err) {\n if ((err as Error)?.name === \"AbortError\") {\n patchLast((turn) => ({ ...turn, isStreaming: false }))\n } else if (err instanceof LainternError) {\n applyFailure(patchLast, {\n code: err.code,\n message: err.message,\n guardrailSlug: null,\n ...(err.rateLimitScope ? { rateLimitScope: err.rateLimitScope } : {}),\n ...(err.rateLimitWindow ? { rateLimitWindow: err.rateLimitWindow } : {}),\n })\n } else {\n applyFailure(patchLast, {\n code: \"NETWORK_ERROR\",\n message: err instanceof Error ? err.message : \"An unknown error occurred.\",\n guardrailSlug: null,\n })\n }\n } finally {\n // A turn always stops streaming, however it ended — including an abort\n // that broke the loop without throwing. Leaving the flag set strands a\n // typing indicator on a turn that will never produce another token.\n const last = state.turns.at(-1)\n if (last?.isStreaming) patchLast((turn) => ({ ...turn, isStreaming: false }))\n if (state.isStreaming) emit({ ...state, isStreaming: false })\n controller = null\n }\n },\n\n async setFeedback(turnId, feedback) {\n const turn = state.turns.find((t) => t.id === turnId)\n if (!turn?.messageId) {\n throw new LainternError(\n \"FEEDBACK_UNAVAILABLE\",\n \"This turn has no message id yet — feedback is only possible once a turn completes.\",\n )\n }\n await client.submitFeedback(turn.messageId, feedback)\n emit({\n ...state,\n turns: state.turns.map((t) => (t.id === turnId ? { ...t, feedback } : t)),\n })\n },\n }\n}\n\n/**\n * A completed step replaces its own \"started\" entry rather than appending, so\n * the UI shows one row per tool call that fills in, not two.\n */\nfunction mergeStep(steps: StepEvent[], step: StepEvent): StepEvent[] {\n if (step.status !== \"completed\") return [...steps, step]\n const startedIndex = steps.findIndex((s) => s.tool === step.tool && s.status === \"started\")\n if (startedIndex === -1) return [...steps, step]\n const next = [...steps]\n next[startedIndex] = step\n return next\n}\n\n/**\n * A failed or blocked turn.\n *\n * A guardrail block discards whatever streamed before it fired: the partial\n * answer is exactly the content the guardrail decided the user should not see,\n * and a hard block always wins over a soft advisory that arrived a moment\n * earlier.\n */\nfunction applyFailure(\n patchLast: (patch: (turn: Turn) => Turn) => void,\n failure: {\n code: string\n message: string\n guardrailSlug: string | null\n rateLimitScope?: \"user\" | \"agent\"\n rateLimitWindow?: \"minute\" | \"hour\" | \"day\"\n },\n): void {\n if (failure.code === \"GUARDRAIL_TRIGGERED\") {\n patchLast((turn) => ({\n ...turn,\n isStreaming: false,\n assistantContent: \"\",\n citations: new Map(),\n sources: [],\n externalSources: {},\n steps: [],\n mode: null,\n advisory: undefined,\n blocked: { slug: failure.guardrailSlug, message: failure.message },\n }))\n return\n }\n\n patchLast((turn) => ({\n ...turn,\n isStreaming: false,\n error: {\n code: failure.code,\n message: failure.message,\n ...(failure.rateLimitScope ? { rateLimitScope: failure.rateLimitScope } : {}),\n ...(failure.rateLimitWindow ? { rateLimitWindow: failure.rateLimitWindow } : {}),\n },\n }))\n}\n","import { useCallback, useMemo, useRef, useSyncExternalStore } from \"react\"\nimport { createConversation } from \"../conversation.js\"\nimport type {\n Conversation,\n ConversationOptions,\n ConversationState,\n SendOptions,\n} from \"../conversation.js\"\nimport type { LainternClient } from \"../client.js\"\n\nexport interface UseLainternChatResult extends ConversationState {\n send: (message: string, options?: SendOptions) => Promise<void>\n abort: () => void\n reset: () => void\n setFeedback: (turnId: string, feedback: \"positive\" | \"negative\") => Promise<void>\n /** The underlying store, if you need it outside React. */\n conversation: Conversation\n}\n\n/**\n * React binding for {@link createConversation}.\n *\n * The conversation lives outside React and is read through\n * `useSyncExternalStore`, so a streaming answer re-renders only the components\n * that read it — and the transcript survives a parent re-render.\n *\n * The client and options are captured on first render: pass a client created\n * once (module scope, or a `useMemo` in your provider), not a fresh one per\n * render.\n */\nexport function useLainternChat(\n client: LainternClient,\n options: ConversationOptions = {},\n): UseLainternChatResult {\n const optionsRef = useRef(options)\n const conversation = useMemo(\n () => createConversation(client, optionsRef.current),\n [client],\n )\n\n const state = useSyncExternalStore(\n useCallback((listener) => conversation.subscribe(listener), [conversation]),\n useCallback(() => conversation.getState(), [conversation]),\n useCallback(() => conversation.getState(), [conversation]),\n )\n\n const send = useCallback(\n (message: string, sendOptions?: SendOptions) => conversation.send(message, sendOptions),\n [conversation],\n )\n const abort = useCallback(() => conversation.abort(), [conversation])\n const reset = useCallback(() => conversation.reset(), [conversation])\n const setFeedback = useCallback(\n (turnId: string, feedback: \"positive\" | \"negative\") =>\n conversation.setFeedback(turnId, feedback),\n [conversation],\n )\n\n return { ...state, send, abort, reset, setFeedback, conversation }\n}\n\nexport type { Conversation, ConversationOptions, ConversationState, SendOptions } from \"../conversation.js\"\nexport type { Turn, TurnAdvisory, TurnBlock, TurnError } from \"../conversation.js\"\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { f as ConversationState, i as SendOptions, d as Conversation, L as LainternClient, e as ConversationOptions } from '../conversation-7VzVDT95.cjs';
|
|
2
|
+
export { n as Turn, o as TurnAdvisory, p as TurnBlock, q as TurnError } from '../conversation-7VzVDT95.cjs';
|
|
3
|
+
|
|
4
|
+
interface UseLainternChatResult extends ConversationState {
|
|
5
|
+
send: (message: string, options?: SendOptions) => Promise<void>;
|
|
6
|
+
abort: () => void;
|
|
7
|
+
reset: () => void;
|
|
8
|
+
setFeedback: (turnId: string, feedback: "positive" | "negative") => Promise<void>;
|
|
9
|
+
/** The underlying store, if you need it outside React. */
|
|
10
|
+
conversation: Conversation;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* React binding for {@link createConversation}.
|
|
14
|
+
*
|
|
15
|
+
* The conversation lives outside React and is read through
|
|
16
|
+
* `useSyncExternalStore`, so a streaming answer re-renders only the components
|
|
17
|
+
* that read it — and the transcript survives a parent re-render.
|
|
18
|
+
*
|
|
19
|
+
* The client and options are captured on first render: pass a client created
|
|
20
|
+
* once (module scope, or a `useMemo` in your provider), not a fresh one per
|
|
21
|
+
* render.
|
|
22
|
+
*/
|
|
23
|
+
declare function useLainternChat(client: LainternClient, options?: ConversationOptions): UseLainternChatResult;
|
|
24
|
+
|
|
25
|
+
export { Conversation, ConversationOptions, ConversationState, SendOptions, type UseLainternChatResult, useLainternChat };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { f as ConversationState, i as SendOptions, d as Conversation, L as LainternClient, e as ConversationOptions } from '../conversation-7VzVDT95.js';
|
|
2
|
+
export { n as Turn, o as TurnAdvisory, p as TurnBlock, q as TurnError } from '../conversation-7VzVDT95.js';
|
|
3
|
+
|
|
4
|
+
interface UseLainternChatResult extends ConversationState {
|
|
5
|
+
send: (message: string, options?: SendOptions) => Promise<void>;
|
|
6
|
+
abort: () => void;
|
|
7
|
+
reset: () => void;
|
|
8
|
+
setFeedback: (turnId: string, feedback: "positive" | "negative") => Promise<void>;
|
|
9
|
+
/** The underlying store, if you need it outside React. */
|
|
10
|
+
conversation: Conversation;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* React binding for {@link createConversation}.
|
|
14
|
+
*
|
|
15
|
+
* The conversation lives outside React and is read through
|
|
16
|
+
* `useSyncExternalStore`, so a streaming answer re-renders only the components
|
|
17
|
+
* that read it — and the transcript survives a parent re-render.
|
|
18
|
+
*
|
|
19
|
+
* The client and options are captured on first render: pass a client created
|
|
20
|
+
* once (module scope, or a `useMemo` in your provider), not a fresh one per
|
|
21
|
+
* render.
|
|
22
|
+
*/
|
|
23
|
+
declare function useLainternChat(client: LainternClient, options?: ConversationOptions): UseLainternChatResult;
|
|
24
|
+
|
|
25
|
+
export { Conversation, ConversationOptions, ConversationState, SendOptions, type UseLainternChatResult, useLainternChat };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createConversation } from '../chunk-WMKNNVMS.js';
|
|
2
|
+
import { useRef, useMemo, useSyncExternalStore, useCallback } from 'react';
|
|
3
|
+
|
|
4
|
+
function useLainternChat(client, options = {}) {
|
|
5
|
+
const optionsRef = useRef(options);
|
|
6
|
+
const conversation = useMemo(
|
|
7
|
+
() => createConversation(client, optionsRef.current),
|
|
8
|
+
[client]
|
|
9
|
+
);
|
|
10
|
+
const state = useSyncExternalStore(
|
|
11
|
+
useCallback((listener) => conversation.subscribe(listener), [conversation]),
|
|
12
|
+
useCallback(() => conversation.getState(), [conversation]),
|
|
13
|
+
useCallback(() => conversation.getState(), [conversation])
|
|
14
|
+
);
|
|
15
|
+
const send = useCallback(
|
|
16
|
+
(message, sendOptions) => conversation.send(message, sendOptions),
|
|
17
|
+
[conversation]
|
|
18
|
+
);
|
|
19
|
+
const abort = useCallback(() => conversation.abort(), [conversation]);
|
|
20
|
+
const reset = useCallback(() => conversation.reset(), [conversation]);
|
|
21
|
+
const setFeedback = useCallback(
|
|
22
|
+
(turnId, feedback) => conversation.setFeedback(turnId, feedback),
|
|
23
|
+
[conversation]
|
|
24
|
+
);
|
|
25
|
+
return { ...state, send, abort, reset, setFeedback, conversation };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { useLainternChat };
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/react/index.ts"],"names":[],"mappings":";;;AA8BO,SAAS,eAAA,CACd,MAAA,EACA,OAAA,GAA+B,EAAC,EACT;AACvB,EAAA,MAAM,UAAA,GAAa,OAAO,OAAO,CAAA;AACjC,EAAA,MAAM,YAAA,GAAe,OAAA;AAAA,IACnB,MAAM,kBAAA,CAAmB,MAAA,EAAQ,UAAA,CAAW,OAAO,CAAA;AAAA,IACnD,CAAC,MAAM;AAAA,GACT;AAEA,EAAA,MAAM,KAAA,GAAQ,oBAAA;AAAA,IACZ,WAAA,CAAY,CAAC,QAAA,KAAa,YAAA,CAAa,UAAU,QAAQ,CAAA,EAAG,CAAC,YAAY,CAAC,CAAA;AAAA,IAC1E,YAAY,MAAM,YAAA,CAAa,UAAS,EAAG,CAAC,YAAY,CAAC,CAAA;AAAA,IACzD,YAAY,MAAM,YAAA,CAAa,UAAS,EAAG,CAAC,YAAY,CAAC;AAAA,GAC3D;AAEA,EAAA,MAAM,IAAA,GAAO,WAAA;AAAA,IACX,CAAC,OAAA,EAAiB,WAAA,KAA8B,YAAA,CAAa,IAAA,CAAK,SAAS,WAAW,CAAA;AAAA,IACtF,CAAC,YAAY;AAAA,GACf;AACA,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM,YAAA,CAAa,OAAM,EAAG,CAAC,YAAY,CAAC,CAAA;AACpE,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM,YAAA,CAAa,OAAM,EAAG,CAAC,YAAY,CAAC,CAAA;AACpE,EAAA,MAAM,WAAA,GAAc,WAAA;AAAA,IAClB,CAAC,MAAA,EAAgB,QAAA,KACf,YAAA,CAAa,WAAA,CAAY,QAAQ,QAAQ,CAAA;AAAA,IAC3C,CAAC,YAAY;AAAA,GACf;AAEA,EAAA,OAAO,EAAE,GAAG,KAAA,EAAO,MAAM,KAAA,EAAO,KAAA,EAAO,aAAa,YAAA,EAAa;AACnE","file":"index.js","sourcesContent":["import { useCallback, useMemo, useRef, useSyncExternalStore } from \"react\"\nimport { createConversation } from \"../conversation.js\"\nimport type {\n Conversation,\n ConversationOptions,\n ConversationState,\n SendOptions,\n} from \"../conversation.js\"\nimport type { LainternClient } from \"../client.js\"\n\nexport interface UseLainternChatResult extends ConversationState {\n send: (message: string, options?: SendOptions) => Promise<void>\n abort: () => void\n reset: () => void\n setFeedback: (turnId: string, feedback: \"positive\" | \"negative\") => Promise<void>\n /** The underlying store, if you need it outside React. */\n conversation: Conversation\n}\n\n/**\n * React binding for {@link createConversation}.\n *\n * The conversation lives outside React and is read through\n * `useSyncExternalStore`, so a streaming answer re-renders only the components\n * that read it — and the transcript survives a parent re-render.\n *\n * The client and options are captured on first render: pass a client created\n * once (module scope, or a `useMemo` in your provider), not a fresh one per\n * render.\n */\nexport function useLainternChat(\n client: LainternClient,\n options: ConversationOptions = {},\n): UseLainternChatResult {\n const optionsRef = useRef(options)\n const conversation = useMemo(\n () => createConversation(client, optionsRef.current),\n [client],\n )\n\n const state = useSyncExternalStore(\n useCallback((listener) => conversation.subscribe(listener), [conversation]),\n useCallback(() => conversation.getState(), [conversation]),\n useCallback(() => conversation.getState(), [conversation]),\n )\n\n const send = useCallback(\n (message: string, sendOptions?: SendOptions) => conversation.send(message, sendOptions),\n [conversation],\n )\n const abort = useCallback(() => conversation.abort(), [conversation])\n const reset = useCallback(() => conversation.reset(), [conversation])\n const setFeedback = useCallback(\n (turnId: string, feedback: \"positive\" | \"negative\") =>\n conversation.setFeedback(turnId, feedback),\n [conversation],\n )\n\n return { ...state, send, abort, reset, setFeedback, conversation }\n}\n\nexport type { Conversation, ConversationOptions, ConversationState, SendOptions } from \"../conversation.js\"\nexport type { Turn, TurnAdvisory, TurnBlock, TurnError } from \"../conversation.js\"\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@laintern/chat-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Client SDK for talking to a Laintern chat agent: token lifecycle, streaming, citations and sources.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"main": "./dist/index.cjs",
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js",
|
|
18
|
+
"require": "./dist/index.cjs"
|
|
19
|
+
},
|
|
20
|
+
"./react": {
|
|
21
|
+
"types": "./dist/react/index.d.ts",
|
|
22
|
+
"import": "./dist/react/index.js",
|
|
23
|
+
"require": "./dist/react/index.cjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"react": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"peerDependenciesMeta": {
|
|
33
|
+
"react": {
|
|
34
|
+
"optional": true
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/react": "19.2.14",
|
|
39
|
+
"react": "19.2.4",
|
|
40
|
+
"tsup": "^8.5.1",
|
|
41
|
+
"typescript": "^5.9.3",
|
|
42
|
+
"vitest": "^4.1.0",
|
|
43
|
+
"@laintern-headless/types": "0.0.0"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18"
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "https://github.com/danekoenders/laintern-headless.git",
|
|
51
|
+
"directory": "packages/chat-sdk"
|
|
52
|
+
},
|
|
53
|
+
"keywords": [
|
|
54
|
+
"laintern",
|
|
55
|
+
"chat",
|
|
56
|
+
"sdk",
|
|
57
|
+
"agent",
|
|
58
|
+
"sse",
|
|
59
|
+
"streaming"
|
|
60
|
+
],
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "tsup",
|
|
63
|
+
"typecheck": "tsc --noEmit",
|
|
64
|
+
"test": "vitest run",
|
|
65
|
+
"clean": "rm -rf dist"
|
|
66
|
+
}
|
|
67
|
+
}
|