@qorejs/qore 1.0.0 → 1.0.2
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 +79 -562
- package/README.zh-CN.md +29 -0
- package/dist/src/core/stream-types.d.ts +17 -0
- package/dist/src/core/stream-types.d.ts.map +1 -1
- package/dist/src/core/stream.d.ts +1 -1
- package/dist/src/core/stream.d.ts.map +1 -1
- package/dist/src/core/stream.js +45 -0
- package/dist/src/core/stream.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js.map +1 -1
- package/docs/api.md +77 -0
- package/docs/architecture.md +56 -0
- package/docs/benchmarks.md +27 -0
- package/docs/comparisons.md +35 -0
- package/docs/concepts.md +67 -0
- package/docs/providers.md +58 -0
- package/docs/react.md +102 -0
- package/docs/runtime.md +86 -0
- package/package.json +9 -3
package/docs/react.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# React Adapter
|
|
2
|
+
|
|
3
|
+
Qore's core runtime is framework-neutral. The React adapter lets React apps consume Qore streams without treating streaming as a special rendering case.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm i @qorejs/qore @qorejs/react
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Why It Exists
|
|
10
|
+
|
|
11
|
+
React is still the UI shell for many production AI products. Qore should not force those teams to switch renderers before they can use `stream = signal`.
|
|
12
|
+
|
|
13
|
+
The adapter keeps Qore in the streaming runtime layer:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
Provider / AsyncIterable
|
|
17
|
+
-> QoreStream
|
|
18
|
+
-> React external store
|
|
19
|
+
-> Component view
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
A `QoreStream` remains the source of truth. React subscribes to it through `useSyncExternalStore`, which is the React-supported bridge for external reactive stores.
|
|
23
|
+
|
|
24
|
+
## useQoreStream
|
|
25
|
+
|
|
26
|
+
Use `useQoreStream` when a component owns the stream lifecycle.
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { stream } from '@qorejs/qore';
|
|
30
|
+
import { useQoreStream } from '@qorejs/react';
|
|
31
|
+
|
|
32
|
+
export function Answer({ prompt }: { prompt: string }) {
|
|
33
|
+
const answer = useQoreStream(
|
|
34
|
+
() => stream(fetch(`/api/chat?prompt=${encodeURIComponent(prompt)}`).then((response) => response.body)),
|
|
35
|
+
[prompt],
|
|
36
|
+
{ initialValue: '' }
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<article>
|
|
41
|
+
<p>{answer.value}</p>
|
|
42
|
+
<small>{answer.status}</small>
|
|
43
|
+
<button onClick={() => answer.abort()}>Stop</button>
|
|
44
|
+
</article>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
When dependencies change, the hook aborts the previous stream and starts a new one. When the component unmounts, the active stream is aborted. Pass `{ enabled: false }` to keep the hook subscribed to an idle snapshot without starting network work.
|
|
50
|
+
|
|
51
|
+
## useQoreStreamSnapshot
|
|
52
|
+
|
|
53
|
+
Use `useQoreStreamSnapshot` when the stream is created outside React and the component only needs a live snapshot.
|
|
54
|
+
|
|
55
|
+
```tsx
|
|
56
|
+
import type { QoreStream } from '@qorejs/qore';
|
|
57
|
+
import { useQoreStreamSnapshot } from '@qorejs/react';
|
|
58
|
+
|
|
59
|
+
export function Transcript({ answer }: { answer: QoreStream<string, string> }) {
|
|
60
|
+
const snapshot = useQoreStreamSnapshot(answer, { initialValue: '' });
|
|
61
|
+
|
|
62
|
+
return <p>{snapshot.value}</p>;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The returned object includes the stream value plus lifecycle fields such as `status`, `error`, `chunks`, `streaming`, `completed`, `failed`, `aborted`, `buffered`, and `dropped`.
|
|
67
|
+
|
|
68
|
+
## useQoreSignalSelector
|
|
69
|
+
|
|
70
|
+
Use `useQoreSignalSelector` when a React component only needs one derived slice of a Qore signal. The selector keeps React renders focused on the value that component actually reads.
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import type { QoreStream } from '@qorejs/qore';
|
|
74
|
+
import { useQoreSignalSelector } from '@qorejs/react';
|
|
75
|
+
|
|
76
|
+
function TokenCounter({ answer }: { answer: QoreStream<string, string> }) {
|
|
77
|
+
const tokenCount = useQoreSignalSelector(answer.chunks, (chunks) => chunks.length);
|
|
78
|
+
return <span>{tokenCount}</span>;
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Pass `isEqual` when the selected value is an object and you want to preserve the previous reference until the meaningful fields change.
|
|
83
|
+
|
|
84
|
+
## useQoreSignal
|
|
85
|
+
|
|
86
|
+
Use `useQoreSignal` for any Qore readonly signal.
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
import type { ReadonlySignal } from '@qorejs/qore';
|
|
90
|
+
import { useQoreSignal } from '@qorejs/react';
|
|
91
|
+
|
|
92
|
+
export function Counter({ count }: { count: ReadonlySignal<number> }) {
|
|
93
|
+
const value = useQoreSignal(count);
|
|
94
|
+
return <span>{value}</span>;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Safety Notes
|
|
99
|
+
|
|
100
|
+
Provider adapters are still intended for server-side or trusted runtimes. In browser React apps, stream from your own SSE or NDJSON endpoint instead of exposing provider API keys.
|
|
101
|
+
|
|
102
|
+
Keep dependency arrays honest. If the stream factory reads `prompt`, `model`, `conversationId`, or auth/session state, include those values in the dependency list.
|
package/docs/runtime.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Runtime
|
|
2
|
+
|
|
3
|
+
The stream runtime is the core of Qore.
|
|
4
|
+
|
|
5
|
+
## Lifecycle
|
|
6
|
+
|
|
7
|
+
Every `QoreStream` exposes readonly lifecycle signals:
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
answer.status(); // idle | pending | streaming | completed | failed | aborted
|
|
11
|
+
answer.error(); // Error | null
|
|
12
|
+
answer.chunkCount(); // number
|
|
13
|
+
answer.buffered(); // queued chunks
|
|
14
|
+
answer.dropped(); // dropped chunks
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Runtime-owned state is readonly from user code, so external callers cannot force inconsistent states.
|
|
18
|
+
|
|
19
|
+
## Backpressure
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
const answer = stream.withBackpressure(source, {
|
|
23
|
+
interval: 16,
|
|
24
|
+
buffer: 8,
|
|
25
|
+
overflow: 'drop-oldest'
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Backpressure controls how quickly chunks commit into the signal and UI.
|
|
30
|
+
|
|
31
|
+
Overflow strategies:
|
|
32
|
+
|
|
33
|
+
- `wait`
|
|
34
|
+
- `drop-oldest`
|
|
35
|
+
- `drop-newest`
|
|
36
|
+
- `error`
|
|
37
|
+
|
|
38
|
+
## Orchestration
|
|
39
|
+
|
|
40
|
+
Qore includes stream composition primitives for agent and realtime flows:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
stream.merge([tokens, toolCalls, status]);
|
|
44
|
+
stream.concat([retrieve, summarize, format]);
|
|
45
|
+
stream.pipe(retrieve, [(docs) => summarize(docs), (summary) => format(summary)]);
|
|
46
|
+
stream.race([openai.chat(q), anthropic.chat(q)]);
|
|
47
|
+
stream.retryable(() => openai.chat(q), { maxRetries: 2 });
|
|
48
|
+
stream.switchMap(promptChanges, (prompt) => openai.chat(prompt));
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The composed result is still a stream signal.
|
|
52
|
+
|
|
53
|
+
## Event Streams
|
|
54
|
+
|
|
55
|
+
Provider streams are only the transport boundary. Agent interfaces need a richer runtime surface: text tokens, tool calls, status updates, reasoning notes, diffs, artifacts, retries, and errors can all be modeled as typed events.
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
const events = stream.events(agent.run(task));
|
|
59
|
+
|
|
60
|
+
const markdown = events.select('text', {
|
|
61
|
+
seed: '',
|
|
62
|
+
reduce: (current, event) => current + event.text
|
|
63
|
+
});
|
|
64
|
+
const toolCalls = events.select('tool_call');
|
|
65
|
+
const status = events.select('status');
|
|
66
|
+
const diffs = events.select('diff', {
|
|
67
|
+
seed: '',
|
|
68
|
+
reduce: (current, event) => current + event.patch
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The full event timeline remains available through `events()`. Each selector is also a stream signal, so one UI region can render the timeline while another region renders only the accumulated markdown or diff.
|
|
73
|
+
|
|
74
|
+
A complete typed example lives in [`examples/agent-event-stream.ts`](../examples/agent-event-stream.ts). It projects one agent event timeline into markdown, status, tool-call, tool-result, diff, and artifact surfaces without creating separate state stores.
|
|
75
|
+
|
|
76
|
+
## Abort
|
|
77
|
+
|
|
78
|
+
Streams can be aborted from the Qore stream object or from provider request options:
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
const controller = new AbortController();
|
|
82
|
+
const answer = stream(openai.chat('hello', { signal: controller.signal }));
|
|
83
|
+
|
|
84
|
+
controller.abort();
|
|
85
|
+
answer.abort();
|
|
86
|
+
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qorejs/qore",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Qore is a
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "Qore is a reactive stream runtime for AI-native interfaces.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/src/index.js",
|
|
7
7
|
"types": "./dist/src/index.d.ts",
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"dist/src",
|
|
17
17
|
"README.md",
|
|
18
|
+
"README.zh-CN.md",
|
|
19
|
+
"docs",
|
|
18
20
|
"LICENSE"
|
|
19
21
|
],
|
|
20
22
|
"sideEffects": false,
|
|
@@ -36,7 +38,9 @@
|
|
|
36
38
|
"publish:preflight": "node ./scripts/publish-preflight.mjs",
|
|
37
39
|
"publish:github": "npm publish --registry=https://npm.pkg.github.com",
|
|
38
40
|
"publish:npm": "node ./scripts/publish-npm.mjs",
|
|
39
|
-
"prepublishOnly": "node ./scripts/release-check.mjs"
|
|
41
|
+
"prepublishOnly": "node ./scripts/release-check.mjs",
|
|
42
|
+
"smoke:react-package": "node ./scripts/react-package-smoke.mjs",
|
|
43
|
+
"check:npm-access": "node ./scripts/check-npm-access.mjs"
|
|
40
44
|
},
|
|
41
45
|
"engines": {
|
|
42
46
|
"node": ">=18.0.0"
|
|
@@ -72,6 +76,8 @@
|
|
|
72
76
|
"devDependencies": {
|
|
73
77
|
"@playwright/test": "^1.60.0",
|
|
74
78
|
"@types/node": "^25.6.0",
|
|
79
|
+
"@types/react": "^19.2.17",
|
|
80
|
+
"react": "^19.2.7",
|
|
75
81
|
"typescript": "^6.0.3"
|
|
76
82
|
}
|
|
77
83
|
}
|