@jskit-ai/assistant-core 0.1.165 → 0.1.166
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/examples/conversation/App.vue +104 -0
- package/examples/conversation/README.md +42 -0
- package/examples/conversation/dist/assets/index-CSTXm6q3.css +1 -0
- package/examples/conversation/dist/assets/index-DGgOLzoo.js +30 -0
- package/examples/conversation/dist/index.html +4 -0
- package/examples/conversation/index.html +2 -0
- package/examples/conversation/main.js +9 -0
- package/examples/conversation/node_modules/.vite/deps/@mdi_js.js +7450 -0
- package/examples/conversation/node_modules/.vite/deps/@mdi_js.js.map +1 -0
- package/examples/conversation/node_modules/.vite/deps/VTooltip-jIxO6ULs.js +2311 -0
- package/examples/conversation/node_modules/.vite/deps/VTooltip-jIxO6ULs.js.map +1 -0
- package/examples/conversation/node_modules/.vite/deps/_metadata.json +70 -0
- package/examples/conversation/node_modules/.vite/deps/defineComponent-DK6LSjDW.js +710 -0
- package/examples/conversation/node_modules/.vite/deps/defineComponent-DK6LSjDW.js.map +1 -0
- package/examples/conversation/node_modules/.vite/deps/hotkey-parsing-DnoZxtbW.js +1214 -0
- package/examples/conversation/node_modules/.vite/deps/hotkey-parsing-DnoZxtbW.js.map +1 -0
- package/examples/conversation/node_modules/.vite/deps/icons-ze2hmg7j.js +98 -0
- package/examples/conversation/node_modules/.vite/deps/icons-ze2hmg7j.js.map +1 -0
- package/examples/conversation/node_modules/.vite/deps/package.json +3 -0
- package/examples/conversation/node_modules/.vite/deps/theme-D78LVcJg.js +1301 -0
- package/examples/conversation/node_modules/.vite/deps/theme-D78LVcJg.js.map +1 -0
- package/examples/conversation/node_modules/.vite/deps/vue.js +2 -0
- package/examples/conversation/node_modules/.vite/deps/vue.runtime.esm-bundler-YcakWsDn.js +8538 -0
- package/examples/conversation/node_modules/.vite/deps/vue.runtime.esm-bundler-YcakWsDn.js.map +1 -0
- package/examples/conversation/node_modules/.vite/deps/vuetify.js +413 -0
- package/examples/conversation/node_modules/.vite/deps/vuetify.js.map +1 -0
- package/examples/conversation/node_modules/.vite/deps/vuetify_components.js +25370 -0
- package/examples/conversation/node_modules/.vite/deps/vuetify_components.js.map +1 -0
- package/examples/conversation/node_modules/.vite/deps/vuetify_directives.js +132 -0
- package/examples/conversation/node_modules/.vite/deps/vuetify_directives.js.map +1 -0
- package/examples/conversation/node_modules/.vite/deps/vuetify_iconsets_mdi-svg.js +70 -0
- package/examples/conversation/node_modules/.vite/deps/vuetify_iconsets_mdi-svg.js.map +1 -0
- package/examples/conversation/package.json +22 -0
- package/examples/conversation/server.js +123 -0
- package/examples/conversation/vite.config.js +8 -0
- package/package.json +59 -7
- package/src/client/conversation/AssistantComposerActions.vue +43 -0
- package/src/client/conversation/AssistantConversationElement.vue +78 -0
- package/src/client/conversation/AssistantProgress.vue +88 -0
- package/src/client/conversation/AssistantPromptInput.vue +467 -0
- package/src/client/conversation/AssistantTranscript.vue +1361 -0
- package/src/client/conversation/LongTextInlineParts.vue +64 -0
- package/src/client/conversation/LongTextPreviewBlocks.vue +324 -0
- package/src/client/conversation/index.js +9 -0
- package/src/client/conversation/submitText.js +29 -0
- package/src/client/conversation/useScrollToBottom.js +151 -0
- package/src/server/conversation/codexClient.js +328 -0
- package/src/server/conversation/codexEvents.js +623 -0
- package/src/server/conversation/codexTurn.js +280 -0
- package/src/server/conversation/index.js +2 -0
- package/src/server/conversation/memoryStorage.js +76 -0
- package/src/server/conversation/normalize.js +7 -0
- package/src/server/conversation/openCodeClient.js +512 -0
- package/src/server/conversation/transcript.js +92 -0
- package/src/shared/conversation/answerChoiceSugar.js +191 -0
- package/src/shared/conversation/index.js +6 -0
- package/src/shared/conversation/longTextBlocks.js +403 -0
- package/src/shared/conversation/numberedQuestionSugar.js +295 -0
- package/src/shared/conversation/scrollFollowState.js +32 -0
- package/src/shared/conversation/thinkingText.js +13 -0
- package/src/shared/conversation/turns.js +30 -0
- package/src/testing/conversationStorageContract.js +35 -0
- package/test/conversation.test.js +57 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-app>
|
|
3
|
+
<main class="example">
|
|
4
|
+
<header><h1>Assistant</h1><span>{{ providerLabel }}</span></header>
|
|
5
|
+
<AssistantConversationElement
|
|
6
|
+
:adapter="adapter" :configuration="configuration"
|
|
7
|
+
:configuration-mode="configurationMode" :configuration-fields="fields"
|
|
8
|
+
>
|
|
9
|
+
<template #hints><p v-if="error" role="alert">{{ error }}</p></template>
|
|
10
|
+
</AssistantConversationElement>
|
|
11
|
+
</main>
|
|
12
|
+
</v-app>
|
|
13
|
+
</template>
|
|
14
|
+
<script setup>
|
|
15
|
+
import { computed, onBeforeUnmount, onMounted, reactive, ref } from "vue";
|
|
16
|
+
import { AssistantConversationElement } from "@jskit-ai/assistant-core/client/conversation";
|
|
17
|
+
|
|
18
|
+
const turns = ref([]), draft = ref(""), loading = ref(true), pending = ref(false), error = ref("");
|
|
19
|
+
const providerLabel = ref(""), configurationMode = ref("hidden"), configuration = ref({ tone: "concise" });
|
|
20
|
+
const fields = [{ name: "tone", label: "Answer style", items: ["concise", "detailed"] }];
|
|
21
|
+
let request = null;
|
|
22
|
+
let disposed = false;
|
|
23
|
+
|
|
24
|
+
async function reload() {
|
|
25
|
+
loading.value = true;
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch("/api/conversation");
|
|
28
|
+
if (!response.ok) throw new Error("Could not load the conversation.");
|
|
29
|
+
const data = await response.json();
|
|
30
|
+
if (disposed) return;
|
|
31
|
+
turns.value = data.turns;
|
|
32
|
+
providerLabel.value = data.providerLabel;
|
|
33
|
+
configurationMode.value = data.configurationMode;
|
|
34
|
+
configuration.value = data.configuration;
|
|
35
|
+
error.value = "";
|
|
36
|
+
} catch (failure) { if (!disposed) error.value = failure.message; }
|
|
37
|
+
finally { if (!disposed) loading.value = false; }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function submit({ configuration: selectedConfiguration }) {
|
|
41
|
+
if (pending.value || !draft.value.trim()) return;
|
|
42
|
+
const text = draft.value;
|
|
43
|
+
pending.value = true;
|
|
44
|
+
error.value = "";
|
|
45
|
+
const controller = new AbortController();
|
|
46
|
+
request = controller;
|
|
47
|
+
try {
|
|
48
|
+
const response = await fetch("/api/messages", {
|
|
49
|
+
method: "POST", headers: { "Content-Type": "application/json" }, signal: controller.signal,
|
|
50
|
+
body: JSON.stringify({ messageId: crypto.randomUUID(), text, configuration: selectedConfiguration })
|
|
51
|
+
});
|
|
52
|
+
if (!response.ok) throw new Error((await response.json()).error || "Could not send.");
|
|
53
|
+
if (draft.value === text) draft.value = "";
|
|
54
|
+
const reader = response.body.getReader(), decoder = new TextDecoder();
|
|
55
|
+
let buffer = "";
|
|
56
|
+
try {
|
|
57
|
+
while (true) {
|
|
58
|
+
const { value, done } = await reader.read();
|
|
59
|
+
if (done) break;
|
|
60
|
+
buffer += decoder.decode(value, { stream: true });
|
|
61
|
+
let newline;
|
|
62
|
+
while ((newline = buffer.indexOf("\n")) >= 0) {
|
|
63
|
+
const event = JSON.parse(buffer.slice(0, newline));
|
|
64
|
+
buffer = buffer.slice(newline + 1);
|
|
65
|
+
if (disposed) continue;
|
|
66
|
+
if (event.type === "snapshot") turns.value = event.turns;
|
|
67
|
+
if (event.type === "error") error.value = event.message;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} finally { reader.releaseLock(); }
|
|
71
|
+
} catch (failure) { if (!disposed) error.value = failure.message; }
|
|
72
|
+
finally { if (!disposed) pending.value = false; request = null; }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function stop() {
|
|
76
|
+
try {
|
|
77
|
+
const response = await fetch("/api/run", { method: "DELETE" });
|
|
78
|
+
if (!response.ok) throw new Error("Could not stop the assistant.");
|
|
79
|
+
} catch (failure) { error.value = failure.message; }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const adapter = reactive({
|
|
83
|
+
conversation: { turns, loading, visible: true, assistantLabel: "Assistant", scrollKey: "example" },
|
|
84
|
+
composer: {
|
|
85
|
+
draft, pending, disabled: pending, submitOnModifierEnter: true,
|
|
86
|
+
canSend: computed(() => !loading.value && !pending.value && Boolean(draft.value.trim())),
|
|
87
|
+
canStop: pending, placeholder: "Ask a question…"
|
|
88
|
+
},
|
|
89
|
+
actions: { reload, submit, stop,
|
|
90
|
+
setDraft: (value) => { draft.value = value; },
|
|
91
|
+
updateConfiguration: (value) => { configuration.value = value; }
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
onMounted(reload);
|
|
95
|
+
onBeforeUnmount(() => { disposed = true; request?.abort(); });
|
|
96
|
+
</script>
|
|
97
|
+
<style>
|
|
98
|
+
html, body, #app { margin: 0; height: 100%; }
|
|
99
|
+
.example { display: flex; flex-direction: column; height: 100dvh; max-width: 60rem; width: 100%; margin: auto; padding: 1rem; box-sizing: border-box; }
|
|
100
|
+
.example header { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: 1rem; }
|
|
101
|
+
.example h1 { font-size: 1.4rem; }
|
|
102
|
+
.example header span { color: #666; font-size: .875rem; }
|
|
103
|
+
.example > .assistant-conversation { flex: 1; min-height: 0; }
|
|
104
|
+
</style>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Standalone conversation example
|
|
2
|
+
|
|
3
|
+
Copy this directory into a new application, then run:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm install
|
|
7
|
+
npm run server
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
In a second terminal:
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm run dev
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Open http://127.0.0.1:5175. `npm run build` builds the client. The default demo
|
|
17
|
+
provider streams a labelled example response and needs no credentials.
|
|
18
|
+
|
|
19
|
+
Optional server environment:
|
|
20
|
+
|
|
21
|
+
| Variable | Meaning |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `ASSISTANT_API_KEY` | Enables the existing JSKIT API-model client; server only. |
|
|
24
|
+
| `ASSISTANT_PROVIDER` | `openai`, `deepseek`, or `anthropic`; default `openai`. |
|
|
25
|
+
| `ASSISTANT_MODEL` | Model available to that provider/account. |
|
|
26
|
+
| `ASSISTANT_CONFIGURATION_MODE` | `hidden`, `readonly`, or `editable`; default `editable`. |
|
|
27
|
+
| `ASSISTANT_STORAGE_MODULE` | Path to an app module exporting async `createStorage()`. Otherwise history is held in memory and disappears on restart. |
|
|
28
|
+
|
|
29
|
+
This is a single-user local template with one conversation. A hosted app must
|
|
30
|
+
derive storage scope from authenticated actor/workspace/conversation ownership,
|
|
31
|
+
authorize every route and attachment, and retain provider-delivery evidence for
|
|
32
|
+
reconciliation after crashes. The example never executes the same message ID
|
|
33
|
+
twice. Retrying a request whose provider delivery is uncertain requires the app
|
|
34
|
+
to resolve that uncertainty first.
|
|
35
|
+
|
|
36
|
+
The server owns model choice, credentials and allowed configuration. Hidden or
|
|
37
|
+
read-only configuration always uses the server's fixed settings, regardless of
|
|
38
|
+
the submitted object. Replace the storage adapter to use your application's
|
|
39
|
+
database or files; the UI has no storage assumptions.
|
|
40
|
+
|
|
41
|
+
See the JSKIT guide **Embeddable assistant conversations** for the complete
|
|
42
|
+
client, storage and provider contracts and reusable storage-adapter checks.
|