@slices.sh/ai-chat 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/LICENSE +21 -0
- package/dist/exports.json +794 -0
- package/dist/fixture/fixture-approval.d.ts +4 -0
- package/dist/fixture/fixture-approval.js +59 -0
- package/dist/fixture/fixture-artifacts.d.ts +2 -0
- package/dist/fixture/fixture-artifacts.js +48 -0
- package/dist/fixture/fixture-question.d.ts +5 -0
- package/dist/fixture/fixture-question.js +93 -0
- package/dist/fixture/fixture-script.d.ts +35 -0
- package/dist/fixture/fixture-script.js +280 -0
- package/dist/fixture/fixture-selection.d.ts +5 -0
- package/dist/fixture/fixture-selection.js +20 -0
- package/dist/fixture/fixture.d.ts +20 -0
- package/dist/fixture/fixture.js +75 -0
- package/dist/fixture/provider.d.ts +15 -0
- package/dist/fixture/provider.js +14 -0
- package/dist/headless/always-allow.d.ts +2 -0
- package/dist/headless/always-allow.js +24 -0
- package/dist/headless/answer.d.ts +6 -0
- package/dist/headless/answer.js +29 -0
- package/dist/headless/approval-error.d.ts +2 -0
- package/dist/headless/approval-error.js +16 -0
- package/dist/headless/approval-reason.d.ts +2 -0
- package/dist/headless/approval-reason.js +13 -0
- package/dist/headless/approval-receipt.d.ts +4 -0
- package/dist/headless/approval-receipt.js +13 -0
- package/dist/headless/approve.d.ts +4 -0
- package/dist/headless/approve.js +15 -0
- package/dist/headless/branch-navigation.d.ts +2 -0
- package/dist/headless/branch-navigation.js +11 -0
- package/dist/headless/branch-next.d.ts +2 -0
- package/dist/headless/branch-next.js +21 -0
- package/dist/headless/branch-page.d.ts +5 -0
- package/dist/headless/branch-page.js +13 -0
- package/dist/headless/branch-previous.d.ts +2 -0
- package/dist/headless/branch-previous.js +21 -0
- package/dist/headless/cancel-edit.d.ts +2 -0
- package/dist/headless/cancel-edit.js +10 -0
- package/dist/headless/cancel-queue-edit.d.ts +2 -0
- package/dist/headless/cancel-queue-edit.js +15 -0
- package/dist/headless/cancel-queued.d.ts +2 -0
- package/dist/headless/cancel-queued.js +15 -0
- package/dist/headless/chat-dictation/confirm.d.ts +2 -0
- package/dist/headless/chat-dictation/confirm.js +14 -0
- package/dist/headless/chat-dictation/index.d.ts +12 -0
- package/dist/headless/chat-dictation/index.js +9 -0
- package/dist/headless/chat-dictation/input.d.ts +3 -0
- package/dist/headless/chat-dictation/input.js +42 -0
- package/dist/headless/chat-dictation/root.d.ts +3 -0
- package/dist/headless/chat-dictation/root.js +42 -0
- package/dist/headless/chat-dictation/text.d.ts +3 -0
- package/dist/headless/chat-dictation/text.js +14 -0
- package/dist/headless/chat-dictation/voice.d.ts +3 -0
- package/dist/headless/chat-dictation/voice.js +35 -0
- package/dist/headless/citation-content.d.ts +3 -0
- package/dist/headless/citation-content.js +19 -0
- package/dist/headless/citation-description.d.ts +2 -0
- package/dist/headless/citation-description.js +12 -0
- package/dist/headless/citation-root.d.ts +2 -0
- package/dist/headless/citation-root.js +13 -0
- package/dist/headless/citation-title.d.ts +2 -0
- package/dist/headless/citation-title.js +12 -0
- package/dist/headless/citation-trigger.d.ts +2 -0
- package/dist/headless/citation-trigger.js +12 -0
- package/dist/headless/clear-answer.d.ts +5 -0
- package/dist/headless/clear-answer.js +19 -0
- package/dist/headless/composer-root.d.ts +14 -0
- package/dist/headless/composer-root.js +12 -0
- package/dist/headless/composer-standalone.d.ts +3 -0
- package/dist/headless/composer-standalone.js +10 -0
- package/dist/headless/composer.d.ts +3 -0
- package/dist/headless/composer.js +24 -0
- package/dist/headless/context-usage.d.ts +3 -0
- package/dist/headless/context-usage.js +13 -0
- package/dist/headless/contexts.d.ts +77 -0
- package/dist/headless/contexts.js +53 -0
- package/dist/headless/continue.d.ts +4 -0
- package/dist/headless/continue.js +26 -0
- package/dist/headless/copy.d.ts +11 -0
- package/dist/headless/copy.js +21 -0
- package/dist/headless/deny.d.ts +4 -0
- package/dist/headless/deny.js +15 -0
- package/dist/headless/edit-queued.d.ts +2 -0
- package/dist/headless/edit-queued.js +15 -0
- package/dist/headless/edit.d.ts +2 -0
- package/dist/headless/edit.js +17 -0
- package/dist/headless/edited-attachments.d.ts +5 -0
- package/dist/headless/edited-attachments.js +11 -0
- package/dist/headless/edited-input.d.ts +2 -0
- package/dist/headless/edited-input.js +28 -0
- package/dist/headless/empty.d.ts +3 -0
- package/dist/headless/empty.js +12 -0
- package/dist/headless/failed.d.ts +15 -0
- package/dist/headless/failed.js +17 -0
- package/dist/headless/feedback-error.d.ts +2 -0
- package/dist/headless/feedback-error.js +11 -0
- package/dist/headless/feedback-root.d.ts +2 -0
- package/dist/headless/feedback-root.js +24 -0
- package/dist/headless/feedback.d.ts +4 -0
- package/dist/headless/feedback.js +18 -0
- package/dist/headless/file-audio.d.ts +2 -0
- package/dist/headless/file-audio.js +16 -0
- package/dist/headless/file-context.d.ts +4 -0
- package/dist/headless/file-context.js +7 -0
- package/dist/headless/file-img.d.ts +2 -0
- package/dist/headless/file-img.js +21 -0
- package/dist/headless/file-kind-gate.d.ts +5 -0
- package/dist/headless/file-kind-gate.js +11 -0
- package/dist/headless/file-link.d.ts +3 -0
- package/dist/headless/file-link.js +14 -0
- package/dist/headless/file-root.d.ts +5 -0
- package/dist/headless/file-root.js +13 -0
- package/dist/headless/file-unavailable.d.ts +2 -0
- package/dist/headless/file-unavailable.js +12 -0
- package/dist/headless/file-video.d.ts +2 -0
- package/dist/headless/file-video.js +16 -0
- package/dist/headless/follow-ups.d.ts +5 -0
- package/dist/headless/follow-ups.js +20 -0
- package/dist/headless/forget-approval.d.ts +2 -0
- package/dist/headless/forget-approval.js +21 -0
- package/dist/headless/gate-content.d.ts +2 -0
- package/dist/headless/gate-content.js +6 -0
- package/dist/headless/index.d.ts +249 -0
- package/dist/headless/index.js +251 -0
- package/dist/headless/input.d.ts +11 -0
- package/dist/headless/input.js +18 -0
- package/dist/headless/jump-to-latest.d.ts +3 -0
- package/dist/headless/jump-to-latest.js +14 -0
- package/dist/headless/message-edit-root.d.ts +2 -0
- package/dist/headless/message-edit-root.js +13 -0
- package/dist/headless/message-editing.d.ts +2 -0
- package/dist/headless/message-editing.js +10 -0
- package/dist/headless/message-editor.d.ts +2 -0
- package/dist/headless/message-editor.js +19 -0
- package/dist/headless/message-footer.d.ts +3 -0
- package/dist/headless/message-footer.js +15 -0
- package/dist/headless/message-reading.d.ts +2 -0
- package/dist/headless/message-reading.js +10 -0
- package/dist/headless/message.d.ts +11 -0
- package/dist/headless/message.js +34 -0
- package/dist/headless/messages.d.ts +9 -0
- package/dist/headless/messages.js +19 -0
- package/dist/headless/model-picker.d.ts +2 -0
- package/dist/headless/model-picker.js +14 -0
- package/dist/headless/model.d.ts +4 -0
- package/dist/headless/model.js +13 -0
- package/dist/headless/models.d.ts +5 -0
- package/dist/headless/models.js +11 -0
- package/dist/headless/optional-tools.d.ts +7 -0
- package/dist/headless/optional-tools.js +12 -0
- package/dist/headless/other-answer.d.ts +5 -0
- package/dist/headless/other-answer.js +32 -0
- package/dist/headless/part-approval.d.ts +2 -0
- package/dist/headless/part-approval.js +33 -0
- package/dist/headless/part-file.d.ts +6 -0
- package/dist/headless/part-file.js +11 -0
- package/dist/headless/part-question.d.ts +2 -0
- package/dist/headless/part-question.js +57 -0
- package/dist/headless/part-reasoning.d.ts +15 -0
- package/dist/headless/part-reasoning.js +26 -0
- package/dist/headless/part-source.d.ts +5 -0
- package/dist/headless/part-source.js +20 -0
- package/dist/headless/part-text.d.ts +16 -0
- package/dist/headless/part-text.js +29 -0
- package/dist/headless/part-tool.d.ts +16 -0
- package/dist/headless/part-tool.js +14 -0
- package/dist/headless/parts.d.ts +11 -0
- package/dist/headless/parts.js +11 -0
- package/dist/headless/question-back.d.ts +2 -0
- package/dist/headless/question-back.js +16 -0
- package/dist/headless/question-error.d.ts +2 -0
- package/dist/headless/question-error.js +14 -0
- package/dist/headless/question-invalid.d.ts +8 -0
- package/dist/headless/question-invalid.js +18 -0
- package/dist/headless/question-item.d.ts +8 -0
- package/dist/headless/question-item.js +51 -0
- package/dist/headless/question-next.d.ts +3 -0
- package/dist/headless/question-next.js +16 -0
- package/dist/headless/question-other.d.ts +5 -0
- package/dist/headless/question-other.js +22 -0
- package/dist/headless/question-progress.d.ts +8 -0
- package/dist/headless/question-progress.js +14 -0
- package/dist/headless/question-receipt.d.ts +11 -0
- package/dist/headless/question-receipt.js +29 -0
- package/dist/headless/questions.d.ts +3 -0
- package/dist/headless/questions.js +13 -0
- package/dist/headless/queue-editing.d.ts +4 -0
- package/dist/headless/queue-editing.js +11 -0
- package/dist/headless/queue-item.d.ts +2 -0
- package/dist/headless/queue-item.js +10 -0
- package/dist/headless/queue-items.d.ts +2 -0
- package/dist/headless/queue-items.js +11 -0
- package/dist/headless/queue-preview.d.ts +6 -0
- package/dist/headless/queue-preview.js +11 -0
- package/dist/headless/queued-input.d.ts +2 -0
- package/dist/headless/queued-input.js +27 -0
- package/dist/headless/queued.d.ts +17 -0
- package/dist/headless/queued.js +16 -0
- package/dist/headless/read-aloud.d.ts +5 -0
- package/dist/headless/read-aloud.js +19 -0
- package/dist/headless/reasoning-disclosure.d.ts +3 -0
- package/dist/headless/reasoning-disclosure.js +17 -0
- package/dist/headless/reasoning-timer.d.ts +5 -0
- package/dist/headless/reasoning-timer.js +13 -0
- package/dist/headless/references.d.ts +6 -0
- package/dist/headless/references.js +13 -0
- package/dist/headless/regenerate.d.ts +3 -0
- package/dist/headless/regenerate.js +22 -0
- package/dist/headless/remembered-approval.d.ts +2 -0
- package/dist/headless/remembered-approval.js +14 -0
- package/dist/headless/root.d.ts +30 -0
- package/dist/headless/root.js +99 -0
- package/dist/headless/save-edit.d.ts +2 -0
- package/dist/headless/save-edit.js +16 -0
- package/dist/headless/save-queued.d.ts +2 -0
- package/dist/headless/save-queued.js +15 -0
- package/dist/headless/send-now.d.ts +2 -0
- package/dist/headless/send-now.js +17 -0
- package/dist/headless/settings.d.ts +2 -0
- package/dist/headless/settings.js +10 -0
- package/dist/headless/skip-questions.d.ts +6 -0
- package/dist/headless/skip-questions.js +18 -0
- package/dist/headless/source-context.d.ts +5 -0
- package/dist/headless/source-context.js +9 -0
- package/dist/headless/source-link.d.ts +2 -0
- package/dist/headless/source-link.js +13 -0
- package/dist/headless/source-primitives.d.ts +1 -0
- package/dist/headless/source-primitives.js +7 -0
- package/dist/headless/source-root.d.ts +5 -0
- package/dist/headless/source-root.js +9 -0
- package/dist/headless/source-sheet-close.d.ts +2 -0
- package/dist/headless/source-sheet-close.js +12 -0
- package/dist/headless/source-sheet-content.d.ts +3 -0
- package/dist/headless/source-sheet-content.js +16 -0
- package/dist/headless/source-sheet-description.d.ts +2 -0
- package/dist/headless/source-sheet-description.js +12 -0
- package/dist/headless/source-sheet-root.d.ts +2 -0
- package/dist/headless/source-sheet-root.js +13 -0
- package/dist/headless/source-sheet-title.d.ts +2 -0
- package/dist/headless/source-sheet-title.js +12 -0
- package/dist/headless/source-sheet-trigger.d.ts +2 -0
- package/dist/headless/source-sheet-trigger.js +12 -0
- package/dist/headless/source-unavailable.d.ts +2 -0
- package/dist/headless/source-unavailable.js +9 -0
- package/dist/headless/sources.d.ts +14 -0
- package/dist/headless/sources.js +12 -0
- package/dist/headless/speaker.d.ts +13 -0
- package/dist/headless/speaker.js +10 -0
- package/dist/headless/step-boundary.d.ts +4 -0
- package/dist/headless/step-boundary.js +16 -0
- package/dist/headless/stop.d.ts +10 -0
- package/dist/headless/stop.js +18 -0
- package/dist/headless/stopped.d.ts +11 -0
- package/dist/headless/stopped.js +17 -0
- package/dist/headless/submit-answers.d.ts +3 -0
- package/dist/headless/submit-answers.js +12 -0
- package/dist/headless/submit.d.ts +23 -0
- package/dist/headless/submit.js +24 -0
- package/dist/headless/suggestion.d.ts +10 -0
- package/dist/headless/suggestion.js +14 -0
- package/dist/headless/suggestions.d.ts +8 -0
- package/dist/headless/suggestions.js +12 -0
- package/dist/headless/thread.d.ts +3 -0
- package/dist/headless/thread.js +25 -0
- package/dist/headless/timing.d.ts +5 -0
- package/dist/headless/timing.js +13 -0
- package/dist/headless/toggle-tool.d.ts +4 -0
- package/dist/headless/toggle-tool.js +15 -0
- package/dist/headless/tool-error.d.ts +4 -0
- package/dist/headless/tool-error.js +13 -0
- package/dist/headless/tool-input.d.ts +4 -0
- package/dist/headless/tool-input.js +13 -0
- package/dist/headless/tool-output.d.ts +4 -0
- package/dist/headless/tool-output.js +13 -0
- package/dist/headless/tool-renderer.d.ts +13 -0
- package/dist/headless/tool-renderer.js +15 -0
- package/dist/headless/transcript.d.ts +23 -0
- package/dist/headless/transcript.js +24 -0
- package/dist/headless/try-again.d.ts +2 -0
- package/dist/headless/try-again.js +24 -0
- package/dist/headless/unavailable.d.ts +8 -0
- package/dist/headless/unavailable.js +9 -0
- package/dist/headless/usage.d.ts +13 -0
- package/dist/headless/usage.js +15 -0
- package/dist/headless/use-merged-ref.d.ts +2 -0
- package/dist/headless/use-merged-ref.js +17 -0
- package/dist/headless/use-question.d.ts +3 -0
- package/dist/headless/use-question.js +9 -0
- package/dist/headless/waiting.d.ts +13 -0
- package/dist/headless/waiting.js +12 -0
- package/dist/headless/writing.d.ts +13 -0
- package/dist/headless/writing.js +12 -0
- package/dist/hooks/chat-refusal.d.ts +18 -0
- package/dist/hooks/chat-refusal.js +44 -0
- package/dist/hooks/turn-mailbox.d.ts +14 -0
- package/dist/hooks/turn-mailbox.js +114 -0
- package/dist/hooks/use-branches.d.ts +10 -0
- package/dist/hooks/use-branches.js +69 -0
- package/dist/hooks/use-chat-instance.d.ts +28 -0
- package/dist/hooks/use-chat-instance.js +44 -0
- package/dist/hooks/use-chat-settings.d.ts +11 -0
- package/dist/hooks/use-chat-settings.js +33 -0
- package/dist/hooks/use-chat-sink.d.ts +19 -0
- package/dist/hooks/use-chat-sink.js +56 -0
- package/dist/hooks/use-composer.d.ts +33 -0
- package/dist/hooks/use-composer.js +45 -0
- package/dist/hooks/use-context-usage.d.ts +8 -0
- package/dist/hooks/use-context-usage.js +14 -0
- package/dist/hooks/use-copied.d.ts +14 -0
- package/dist/hooks/use-copied.js +22 -0
- package/dist/hooks/use-draft.d.ts +6 -0
- package/dist/hooks/use-draft.js +38 -0
- package/dist/hooks/use-feedback.d.ts +8 -0
- package/dist/hooks/use-feedback.js +28 -0
- package/dist/hooks/use-follow.d.ts +15 -0
- package/dist/hooks/use-follow.js +30 -0
- package/dist/hooks/use-media-load.d.ts +5 -0
- package/dist/hooks/use-media-load.js +12 -0
- package/dist/hooks/use-media.d.ts +3 -0
- package/dist/hooks/use-media.js +9 -0
- package/dist/hooks/use-question-check.d.ts +11 -0
- package/dist/hooks/use-question-check.js +29 -0
- package/dist/hooks/use-question-draft.d.ts +34 -0
- package/dist/hooks/use-question-draft.js +57 -0
- package/dist/hooks/use-question-steps.d.ts +18 -0
- package/dist/hooks/use-question-steps.js +31 -0
- package/dist/hooks/use-question-write.d.ts +8 -0
- package/dist/hooks/use-question-write.js +25 -0
- package/dist/hooks/use-read-aloud.d.ts +8 -0
- package/dist/hooks/use-read-aloud.js +60 -0
- package/dist/hooks/use-reasoning.d.ts +8 -0
- package/dist/hooks/use-reasoning.js +32 -0
- package/dist/hooks/use-remembered-approvals.d.ts +9 -0
- package/dist/hooks/use-remembered-approvals.js +40 -0
- package/dist/hooks/use-reply.d.ts +3 -0
- package/dist/hooks/use-reply.js +9 -0
- package/dist/hooks/use-report-missing-configuration.d.ts +9 -0
- package/dist/hooks/use-report-missing-configuration.js +14 -0
- package/dist/hooks/use-retry-delay.d.ts +5 -0
- package/dist/hooks/use-retry-delay.js +17 -0
- package/dist/hooks/use-sent-draft.d.ts +8 -0
- package/dist/hooks/use-sent-draft.js +25 -0
- package/dist/hooks/use-source-disclosure.d.ts +5 -0
- package/dist/hooks/use-source-disclosure.js +9 -0
- package/dist/hooks/use-sources.d.ts +3 -0
- package/dist/hooks/use-sources.js +9 -0
- package/dist/hooks/use-tool-approval-memory.d.ts +8 -0
- package/dist/hooks/use-tool-approval-memory.js +16 -0
- package/dist/hooks/use-tool-approval.d.ts +10 -0
- package/dist/hooks/use-tool-approval.js +26 -0
- package/dist/hooks/use-turn-queue.d.ts +32 -0
- package/dist/hooks/use-turn-queue.js +33 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +5 -0
- package/dist/next.d.ts +43 -0
- package/dist/next.js +10 -0
- package/dist/services/approval-memory.d.ts +9 -0
- package/dist/services/approval-memory.js +32 -0
- package/dist/services/approval.d.ts +14 -0
- package/dist/services/approval.js +37 -0
- package/dist/services/branches.d.ts +27 -0
- package/dist/services/branches.js +131 -0
- package/dist/services/chat-session.d.ts +20 -0
- package/dist/services/chat-session.js +61 -0
- package/dist/services/choices.d.ts +31 -0
- package/dist/services/choices.js +39 -0
- package/dist/services/continue.d.ts +8 -0
- package/dist/services/continue.js +24 -0
- package/dist/services/errors.d.ts +52 -0
- package/dist/services/errors.js +54 -0
- package/dist/services/media.d.ts +9 -0
- package/dist/services/media.js +19 -0
- package/dist/services/messages.d.ts +44 -0
- package/dist/services/messages.js +0 -0
- package/dist/services/optional.d.ts +7 -0
- package/dist/services/optional.js +7 -0
- package/dist/services/ports.d.ts +31 -0
- package/dist/services/ports.js +0 -0
- package/dist/services/questions.d.ts +67 -0
- package/dist/services/questions.js +119 -0
- package/dist/services/request.d.ts +26 -0
- package/dist/services/request.js +85 -0
- package/dist/services/selection.d.ts +10 -0
- package/dist/services/selection.js +49 -0
- package/dist/services/sources.d.ts +11 -0
- package/dist/services/sources.js +29 -0
- package/dist/services/tools.d.ts +11 -0
- package/dist/services/tools.js +0 -0
- package/dist/services/turn.d.ts +84 -0
- package/dist/services/turn.js +0 -0
- package/dist/services/types.d.ts +23 -0
- package/dist/services/types.js +15 -0
- package/package.json +822 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { isToolUIPart } from "ai";
|
|
2
|
+
function approvalHistory() {
|
|
3
|
+
return [
|
|
4
|
+
{ id: "approval-user", role: "user", parts: [{ type: "text", text: "Count the words in my draft." }] },
|
|
5
|
+
{
|
|
6
|
+
id: "approval-answer",
|
|
7
|
+
role: "assistant",
|
|
8
|
+
parts: [
|
|
9
|
+
{
|
|
10
|
+
type: "tool-count_words",
|
|
11
|
+
toolCallId: "approval-call",
|
|
12
|
+
state: "approval-requested",
|
|
13
|
+
input: { text: "A short draft" },
|
|
14
|
+
approval: { id: "approval-demo", requestReason: "This tool is configured to ask before it runs." }
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
];
|
|
19
|
+
}
|
|
20
|
+
function approvalTurn(messages) {
|
|
21
|
+
const last = messages.at(-1);
|
|
22
|
+
const decided = last?.parts.find((part) => isToolUIPart(part) && part.state === "approval-responded");
|
|
23
|
+
if (decided && isToolUIPart(decided) && decided.state === "approval-responded") {
|
|
24
|
+
const approved = decided.approval.approved;
|
|
25
|
+
return [
|
|
26
|
+
{ type: "start", messageId: last.id },
|
|
27
|
+
approved ? { type: "tool-output-available", toolCallId: decided.toolCallId, output: { words: 3 } } : { type: "tool-output-denied", toolCallId: decided.toolCallId },
|
|
28
|
+
{ type: "text-start", id: "decision" },
|
|
29
|
+
{
|
|
30
|
+
type: "text-delta",
|
|
31
|
+
id: "decision",
|
|
32
|
+
delta: approved ? "The tool was allowed and counted 3 words." : "The tool was denied and did not run."
|
|
33
|
+
},
|
|
34
|
+
{ type: "text-end", id: "decision" },
|
|
35
|
+
{ type: "finish" }
|
|
36
|
+
];
|
|
37
|
+
}
|
|
38
|
+
const id = last?.id ?? "empty";
|
|
39
|
+
return [
|
|
40
|
+
{ type: "start", messageId: "answer-" + id },
|
|
41
|
+
{
|
|
42
|
+
type: "tool-input-available",
|
|
43
|
+
toolCallId: "call-" + id,
|
|
44
|
+
toolName: "count_words",
|
|
45
|
+
input: { text: "A short draft" }
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: "tool-approval-request",
|
|
49
|
+
approvalId: "approval-" + id,
|
|
50
|
+
toolCallId: "call-" + id,
|
|
51
|
+
reason: "This tool is configured to ask before it runs."
|
|
52
|
+
},
|
|
53
|
+
{ type: "finish", finishReason: "tool-calls" }
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
approvalHistory,
|
|
58
|
+
approvalTurn
|
|
59
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
function artifactHistory() {
|
|
2
|
+
return [
|
|
3
|
+
{
|
|
4
|
+
id: "artifact-user",
|
|
5
|
+
role: "user",
|
|
6
|
+
parts: [{ type: "text", text: "Build a counter, then increase its initial value." }]
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
id: "artifact-first",
|
|
10
|
+
role: "assistant",
|
|
11
|
+
parts: [
|
|
12
|
+
{
|
|
13
|
+
type: "tool-createArtifact",
|
|
14
|
+
toolCallId: "artifact-v1",
|
|
15
|
+
state: "output-available",
|
|
16
|
+
input: {
|
|
17
|
+
artifactId: "counter",
|
|
18
|
+
title: "Counter",
|
|
19
|
+
code: "import React from 'react'\nexport default function App() { return <button>1</button> }",
|
|
20
|
+
summary: "First version"
|
|
21
|
+
},
|
|
22
|
+
output: { artifactId: "counter", recorded: true }
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: "artifact-second",
|
|
28
|
+
role: "assistant",
|
|
29
|
+
parts: [
|
|
30
|
+
{
|
|
31
|
+
type: "tool-updateArtifact",
|
|
32
|
+
toolCallId: "artifact-v2",
|
|
33
|
+
state: "output-available",
|
|
34
|
+
input: {
|
|
35
|
+
artifactId: "counter",
|
|
36
|
+
title: "Counter",
|
|
37
|
+
code: "import React from 'react'\nexport default function App() { return <button>2</button> }",
|
|
38
|
+
summary: "Second version"
|
|
39
|
+
},
|
|
40
|
+
output: { artifactId: "counter", recorded: true }
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
artifactHistory
|
|
48
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type UIMessageChunk } from "ai";
|
|
2
|
+
import { type AiChatMessage, type Questionnaire } from "../services/types";
|
|
3
|
+
export declare const QUESTIONS: Questionnaire;
|
|
4
|
+
export declare function questionHistory(): AiChatMessage[];
|
|
5
|
+
export declare function questionTurn(messages: AiChatMessage[]): UIMessageChunk[];
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { getToolName, isToolUIPart } from "ai";
|
|
2
|
+
import { isSkippedQuestion } from "../services/types.js";
|
|
3
|
+
const QUESTIONS = {
|
|
4
|
+
title: "Plan your draft",
|
|
5
|
+
questions: [
|
|
6
|
+
{
|
|
7
|
+
name: "tone",
|
|
8
|
+
title: "Which tone?",
|
|
9
|
+
required: true,
|
|
10
|
+
choices: [
|
|
11
|
+
{ value: "brief", label: "Brief" },
|
|
12
|
+
{ value: "detailed", label: "Detailed" }
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "topics",
|
|
17
|
+
title: "Which topics?",
|
|
18
|
+
multiple: true,
|
|
19
|
+
choices: [
|
|
20
|
+
{ value: "design", label: "Design" },
|
|
21
|
+
{ value: "engineering", label: "Engineering" }
|
|
22
|
+
],
|
|
23
|
+
other: "Another topic"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "audience",
|
|
27
|
+
title: "Who is it for?",
|
|
28
|
+
description: "The closest fit is enough.",
|
|
29
|
+
choices: [
|
|
30
|
+
{ value: "team", label: "My team", description: "People who know the context" },
|
|
31
|
+
{ value: "public", label: "The public", description: "Readers who start from nothing" }
|
|
32
|
+
],
|
|
33
|
+
other: "Someone else"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
};
|
|
37
|
+
function questionHistory() {
|
|
38
|
+
return [
|
|
39
|
+
{ id: "question-user", role: "user", parts: [{ type: "text", text: "Help me prepare a draft." }] },
|
|
40
|
+
{
|
|
41
|
+
id: "question-answer",
|
|
42
|
+
role: "assistant",
|
|
43
|
+
parts: [
|
|
44
|
+
{
|
|
45
|
+
type: "tool-askQuestions",
|
|
46
|
+
toolCallId: "question-call",
|
|
47
|
+
state: "input-available",
|
|
48
|
+
input: QUESTIONS
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
function questionTurn(messages) {
|
|
55
|
+
const last = messages.at(-1);
|
|
56
|
+
const answered = last?.parts.find(
|
|
57
|
+
(part) => isToolUIPart(part) && getToolName(part) === "askQuestions" && part.state === "output-available"
|
|
58
|
+
);
|
|
59
|
+
if (messages.some((message) => message.parts.some(isSkippedQuestion)))
|
|
60
|
+
return [
|
|
61
|
+
{ type: "start", messageId: last?.role === "assistant" ? last.id : "skipped-" + (last?.id ?? "empty") },
|
|
62
|
+
{ type: "start-step" },
|
|
63
|
+
{ type: "text-start", id: "answer" },
|
|
64
|
+
{ type: "text-delta", id: "answer", delta: "No problem. I will carry on without your answers." },
|
|
65
|
+
{ type: "text-end", id: "answer" },
|
|
66
|
+
{ type: "finish" }
|
|
67
|
+
];
|
|
68
|
+
if (answered)
|
|
69
|
+
return [
|
|
70
|
+
{ type: "start", messageId: last.id },
|
|
71
|
+
{ type: "start-step" },
|
|
72
|
+
{ type: "text-start", id: "answer" },
|
|
73
|
+
{ type: "text-delta", id: "answer", delta: "Thanks. I will use your answers for the draft." },
|
|
74
|
+
{ type: "text-end", id: "answer" },
|
|
75
|
+
{ type: "finish" }
|
|
76
|
+
];
|
|
77
|
+
const id = last?.id ?? "empty";
|
|
78
|
+
return [
|
|
79
|
+
{ type: "start", messageId: "answer-" + id },
|
|
80
|
+
{
|
|
81
|
+
type: "tool-input-available",
|
|
82
|
+
toolCallId: "question-" + id,
|
|
83
|
+
toolName: "askQuestions",
|
|
84
|
+
input: QUESTIONS
|
|
85
|
+
},
|
|
86
|
+
{ type: "finish", finishReason: "tool-calls" }
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
QUESTIONS,
|
|
91
|
+
questionHistory,
|
|
92
|
+
questionTurn
|
|
93
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { UIMessageChunk } from "ai";
|
|
2
|
+
import { type MissingConfigurationAnswer } from "@slices.sh/core/configuration";
|
|
3
|
+
import { ChatError, type AiChatMessage } from "../services/types";
|
|
4
|
+
/**
|
|
5
|
+
* What a fixture chat does, per scenario.
|
|
6
|
+
*
|
|
7
|
+
* A scenario is a state the screen can be in, not a story: `failed` is what the error banner
|
|
8
|
+
* looks like, `rate-limited` is the one refusal with a different shape of answer, `slow` is
|
|
9
|
+
* the one where stopping mid-answer is worth trying, `rich` is a turn with everything on it —
|
|
10
|
+
* markdown, a reasoning trace, a tool call, citations and a token count — and `interrupted`
|
|
11
|
+
* is what a reply that was stopped looks like after the fact. `missing-configuration` is a
|
|
12
|
+
* server that has no key for its model, which answers `config/missing` rather than a refusal
|
|
13
|
+
* of its own.
|
|
14
|
+
*/
|
|
15
|
+
export declare const CHAT_SCENARIOS: readonly ["ready", "approval", "questions", "empty", "rich", "artifacts", "slow", "interrupted", "failed", "rate-limited", "quota-exceeded", "unknown-chat", "missing-configuration"];
|
|
16
|
+
export type ChatScenario = (typeof CHAT_SCENARIOS)[number];
|
|
17
|
+
export type Script = {
|
|
18
|
+
/** What the transcript already holds when the screen mounts. */
|
|
19
|
+
history: AiChatMessage[];
|
|
20
|
+
/** Milliseconds between words. */
|
|
21
|
+
pace: number;
|
|
22
|
+
refuse: ChatError | MissingConfigurationAnswer | null;
|
|
23
|
+
/** Whether the answer arrives with a trace, a tool call, citations and a count. */
|
|
24
|
+
rich: boolean;
|
|
25
|
+
};
|
|
26
|
+
export declare function scriptFor(scenario: ChatScenario): Script;
|
|
27
|
+
/**
|
|
28
|
+
* The answer a question gets, as the chunks a route streams for it.
|
|
29
|
+
*
|
|
30
|
+
* The same vocabulary a model's turn arrives in, in the same order — a `start`, a trace
|
|
31
|
+
* opened and closed, a tool call's input and then its output under one id, the prose, the
|
|
32
|
+
* citations, and a `finish` carrying what it cost — so the chat state the SDK builds from it
|
|
33
|
+
* is the state it builds from a provider.
|
|
34
|
+
*/
|
|
35
|
+
export declare function turnOf(messages: AiChatMessage[], rich: boolean): UIMessageChunk[];
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { artifactHistory } from "./fixture-artifacts.js";
|
|
2
|
+
import { questionHistory } from "./fixture-question.js";
|
|
3
|
+
import { approvalHistory } from "./fixture-approval.js";
|
|
4
|
+
import { MISSING_CONFIGURATION } from "@slices.sh/core/configuration";
|
|
5
|
+
import { ChatError } from "../services/types.js";
|
|
6
|
+
const CHAT_SCENARIOS = [
|
|
7
|
+
"ready",
|
|
8
|
+
"approval",
|
|
9
|
+
"questions",
|
|
10
|
+
"empty",
|
|
11
|
+
"rich",
|
|
12
|
+
"artifacts",
|
|
13
|
+
"slow",
|
|
14
|
+
"interrupted",
|
|
15
|
+
"failed",
|
|
16
|
+
"rate-limited",
|
|
17
|
+
"quota-exceeded",
|
|
18
|
+
"unknown-chat",
|
|
19
|
+
"missing-configuration"
|
|
20
|
+
];
|
|
21
|
+
const CONVERSATION = [
|
|
22
|
+
said("you-1", "user", "What is in this transcript?"),
|
|
23
|
+
said(
|
|
24
|
+
"answer-1",
|
|
25
|
+
"assistant",
|
|
26
|
+
"Two messages, so far: yours and this one. Everything you see is the AI SDK's own chat and the real parts \u2014 only the backend is a fixture, so the stream, the stop button and the error states behave the way they will against a model."
|
|
27
|
+
)
|
|
28
|
+
];
|
|
29
|
+
const RICH = [
|
|
30
|
+
{
|
|
31
|
+
id: "you-1",
|
|
32
|
+
role: "user",
|
|
33
|
+
parts: [
|
|
34
|
+
{
|
|
35
|
+
type: "file",
|
|
36
|
+
filename: "brief.pdf",
|
|
37
|
+
mediaType: "application/pdf",
|
|
38
|
+
url: "https://example.com/brief.pdf"
|
|
39
|
+
},
|
|
40
|
+
{ type: "text", text: "Show me what a turn can carry." }
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "answer-1",
|
|
45
|
+
role: "assistant",
|
|
46
|
+
parts: [
|
|
47
|
+
{ type: "step-start" },
|
|
48
|
+
{ type: "reasoning", text: "Read the contract, then answer with one of each part.", state: "done" },
|
|
49
|
+
{
|
|
50
|
+
type: "tool-search_docs",
|
|
51
|
+
toolCallId: "search",
|
|
52
|
+
state: "output-available",
|
|
53
|
+
input: { query: "error slugs" },
|
|
54
|
+
output: { found: 3 }
|
|
55
|
+
},
|
|
56
|
+
{ type: "step-start" },
|
|
57
|
+
{
|
|
58
|
+
type: "text",
|
|
59
|
+
state: "done",
|
|
60
|
+
text: [
|
|
61
|
+
"A turn carries **more than prose**. This message has all of it:",
|
|
62
|
+
"",
|
|
63
|
+
"| Part | Where it came from |",
|
|
64
|
+
"|---|---|",
|
|
65
|
+
"| reasoning | `reasoning-delta`, collapsed once the answer starts |",
|
|
66
|
+
"| tool | `tool-input-available` then `tool-output-available`, one card either way |",
|
|
67
|
+
"| source | `source-url`, collected into the footer below |",
|
|
68
|
+
"",
|
|
69
|
+
"```ts",
|
|
70
|
+
"const { messages, status, stop } = useChat({ chat })",
|
|
71
|
+
"```",
|
|
72
|
+
"",
|
|
73
|
+
"- markdown renders while it arrives",
|
|
74
|
+
"",
|
|
75
|
+
"An optional formula: $$E = mc^2$$.",
|
|
76
|
+
"",
|
|
77
|
+
"```mermaid",
|
|
78
|
+
"flowchart LR",
|
|
79
|
+
" Draft --> Review --> Publish",
|
|
80
|
+
"```",
|
|
81
|
+
"- a fence the next token would have closed still renders as code"
|
|
82
|
+
].join("\n")
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: "source-url",
|
|
86
|
+
sourceId: "s1",
|
|
87
|
+
url: "https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol",
|
|
88
|
+
title: "Stream protocol"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
type: "source-url",
|
|
92
|
+
sourceId: "s2",
|
|
93
|
+
url: "https://ai-sdk.dev/docs/ai-sdk-ui/chatbot-tool-usage",
|
|
94
|
+
title: "Tool usage"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
type: "file",
|
|
98
|
+
filename: "fixture.png",
|
|
99
|
+
mediaType: "image/png",
|
|
100
|
+
url: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+jRzEAAAAASUVORK5CYII="
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
type: "file",
|
|
104
|
+
filename: "guide.pdf",
|
|
105
|
+
mediaType: "application/pdf",
|
|
106
|
+
url: "https://example.com/guide.pdf"
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
metadata: {
|
|
110
|
+
usage: counted(412, 190),
|
|
111
|
+
contextTokens: 412,
|
|
112
|
+
contextWindow: 8192,
|
|
113
|
+
model: "fixture-model",
|
|
114
|
+
followUps: ["Show a concrete example", "Summarize the key points"],
|
|
115
|
+
timing: { firstTokenMs: 250, durationMs: 1500 }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
];
|
|
119
|
+
const INTERRUPTED = [
|
|
120
|
+
said("you-1", "user", "Write me the whole contract."),
|
|
121
|
+
{
|
|
122
|
+
id: "answer-1",
|
|
123
|
+
role: "assistant",
|
|
124
|
+
parts: [
|
|
125
|
+
{
|
|
126
|
+
type: "text",
|
|
127
|
+
text: "The contract is one operation \u2014 run a turn \u2014 over a chat this app has config",
|
|
128
|
+
state: "streaming"
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
}
|
|
132
|
+
];
|
|
133
|
+
function scriptFor(scenario) {
|
|
134
|
+
const plain = { refuse: null, rich: false };
|
|
135
|
+
switch (scenario) {
|
|
136
|
+
case "artifacts":
|
|
137
|
+
return { history: artifactHistory(), pace: 5, ...plain };
|
|
138
|
+
case "questions":
|
|
139
|
+
return { history: questionHistory(), pace: 5, ...plain };
|
|
140
|
+
case "approval":
|
|
141
|
+
return { history: approvalHistory(), pace: 5, ...plain };
|
|
142
|
+
case "empty":
|
|
143
|
+
return { history: [], pace: 18, ...plain };
|
|
144
|
+
case "rich":
|
|
145
|
+
return { history: RICH, pace: 24, refuse: null, rich: true };
|
|
146
|
+
case "slow":
|
|
147
|
+
return { history: CONVERSATION, pace: 90, ...plain };
|
|
148
|
+
case "interrupted":
|
|
149
|
+
return { history: INTERRUPTED, pace: 18, ...plain };
|
|
150
|
+
case "failed":
|
|
151
|
+
return {
|
|
152
|
+
history: CONVERSATION,
|
|
153
|
+
pace: 18,
|
|
154
|
+
rich: false,
|
|
155
|
+
refuse: new ChatError("ai-chat/model-unavailable", "The model is not answering right now.")
|
|
156
|
+
};
|
|
157
|
+
case "rate-limited":
|
|
158
|
+
return {
|
|
159
|
+
history: CONVERSATION,
|
|
160
|
+
pace: 18,
|
|
161
|
+
rich: false,
|
|
162
|
+
refuse: new ChatError("ai-chat/rate-limited", "That is a lot of questions. Try again in a minute.")
|
|
163
|
+
};
|
|
164
|
+
case "quota-exceeded":
|
|
165
|
+
return {
|
|
166
|
+
history: CONVERSATION,
|
|
167
|
+
pace: 18,
|
|
168
|
+
rich: false,
|
|
169
|
+
refuse: new ChatError(
|
|
170
|
+
"ai-chat/quota-exceeded",
|
|
171
|
+
"This workspace has used its messages for the month."
|
|
172
|
+
)
|
|
173
|
+
};
|
|
174
|
+
case "unknown-chat":
|
|
175
|
+
return {
|
|
176
|
+
history: [],
|
|
177
|
+
pace: 18,
|
|
178
|
+
rich: false,
|
|
179
|
+
refuse: new ChatError("ai-chat/unknown-chat", "No chat is configured under that name.")
|
|
180
|
+
};
|
|
181
|
+
case "missing-configuration":
|
|
182
|
+
return {
|
|
183
|
+
history: CONVERSATION,
|
|
184
|
+
pace: 18,
|
|
185
|
+
rich: false,
|
|
186
|
+
refuse: {
|
|
187
|
+
error: MISSING_CONFIGURATION,
|
|
188
|
+
scope: "ai-chat",
|
|
189
|
+
message: "This component requires extra server configuration to work properly. If you're the developer, see the logs. If you're a user, please report it!"
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
default:
|
|
193
|
+
return { history: CONVERSATION, pace: 18, ...plain };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function turnOf(messages, rich) {
|
|
197
|
+
const asked = askedIn(messages);
|
|
198
|
+
const chunks = [{ type: "start", messageId: `answer-${messages.length}` }];
|
|
199
|
+
if (rich) {
|
|
200
|
+
chunks.push(
|
|
201
|
+
{ type: "reasoning-start", id: "plan" },
|
|
202
|
+
...pieces("Count the words, then answer in markdown.").map((delta) => ({
|
|
203
|
+
type: "reasoning-delta",
|
|
204
|
+
id: "plan",
|
|
205
|
+
delta
|
|
206
|
+
})),
|
|
207
|
+
{ type: "reasoning-end", id: "plan" },
|
|
208
|
+
{ type: "tool-input-available", toolCallId: "count", toolName: "count_words", input: { text: asked } },
|
|
209
|
+
{ type: "tool-output-available", toolCallId: "count", output: { words: wordsIn(asked) } }
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
chunks.push(
|
|
213
|
+
{ type: "text-start", id: "answer" },
|
|
214
|
+
...pieces(answerTo(asked)).map((delta) => ({ type: "text-delta", id: "answer", delta })),
|
|
215
|
+
{ type: "text-end", id: "answer" }
|
|
216
|
+
);
|
|
217
|
+
if (rich)
|
|
218
|
+
chunks.push({
|
|
219
|
+
type: "source-url",
|
|
220
|
+
sourceId: "protocol",
|
|
221
|
+
url: "https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol",
|
|
222
|
+
title: "Stream protocol"
|
|
223
|
+
});
|
|
224
|
+
chunks.push({
|
|
225
|
+
type: "finish",
|
|
226
|
+
finishReason: "stop",
|
|
227
|
+
...rich ? {
|
|
228
|
+
messageMetadata: {
|
|
229
|
+
usage: counted(412, 190),
|
|
230
|
+
contextTokens: 412,
|
|
231
|
+
contextWindow: 8192,
|
|
232
|
+
model: "fixture-model",
|
|
233
|
+
followUps: ["Show a concrete example", "Summarize the key points"],
|
|
234
|
+
timing: { firstTokenMs: 250, durationMs: 1500 }
|
|
235
|
+
}
|
|
236
|
+
} : {}
|
|
237
|
+
});
|
|
238
|
+
return chunks;
|
|
239
|
+
}
|
|
240
|
+
function answerTo(asked) {
|
|
241
|
+
const words = wordsIn(asked);
|
|
242
|
+
return [
|
|
243
|
+
`You asked ${words} word${words === 1 ? "" : "s"}: **"${asked.trim()}"**`,
|
|
244
|
+
"",
|
|
245
|
+
"This preview has no model behind it. What it does have is the same chat, transcript,",
|
|
246
|
+
"composer, status and error parts the installed component has \u2014 so stopping this",
|
|
247
|
+
"mid-sentence, or typing again before it finishes, does here what it will do in your app."
|
|
248
|
+
].join("\n");
|
|
249
|
+
}
|
|
250
|
+
function askedIn(messages) {
|
|
251
|
+
const asked = messages.findLast((message) => message.role === "user");
|
|
252
|
+
return (asked?.parts ?? []).map((part) => part.type === "text" ? part.text : "").join("");
|
|
253
|
+
}
|
|
254
|
+
function wordsIn(text) {
|
|
255
|
+
return text.trim().split(/\s+/).filter(Boolean).length;
|
|
256
|
+
}
|
|
257
|
+
function pieces(text) {
|
|
258
|
+
return text.split(/(?<=\s)/);
|
|
259
|
+
}
|
|
260
|
+
function counted(input, output) {
|
|
261
|
+
return {
|
|
262
|
+
inputTokens: input,
|
|
263
|
+
inputTokenDetails: { noCacheTokens: input, cacheReadTokens: void 0, cacheWriteTokens: void 0 },
|
|
264
|
+
outputTokens: output,
|
|
265
|
+
outputTokenDetails: { textTokens: output, reasoningTokens: void 0 },
|
|
266
|
+
totalTokens: input + output
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
function said(id, role, text) {
|
|
270
|
+
return {
|
|
271
|
+
id,
|
|
272
|
+
role,
|
|
273
|
+
parts: [{ type: "text", text, ...role === "assistant" ? { state: "done" } : {} }]
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
export {
|
|
277
|
+
CHAT_SCENARIOS,
|
|
278
|
+
scriptFor,
|
|
279
|
+
turnOf
|
|
280
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { UIMessageChunk } from "ai";
|
|
2
|
+
import type { ChatChoices, ChatSelection } from "../services/types";
|
|
3
|
+
export declare const fixtureChoices: ChatChoices;
|
|
4
|
+
/** Scripted model variants and actual optional tool chunks, never pretend live inference. */
|
|
5
|
+
export declare function chosenChunks(chunks: UIMessageChunk[], selection?: ChatSelection): UIMessageChunk[];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const fixtureChoices = {
|
|
2
|
+
models: [
|
|
3
|
+
{ id: "", label: "Standard fixture", contextWindow: 8192 },
|
|
4
|
+
{ id: "detailed", label: "Detailed fixture", contextWindow: 8192 }
|
|
5
|
+
],
|
|
6
|
+
tools: [{ id: "count_words", label: "Word count", defaultEnabled: true }]
|
|
7
|
+
};
|
|
8
|
+
function chosenChunks(chunks, selection) {
|
|
9
|
+
return chunks.flatMap((chunk) => {
|
|
10
|
+
if (selection?.tools && !selection.tools.includes("count_words") && "toolCallId" in chunk && chunk.toolCallId === "count") return [];
|
|
11
|
+
if (selection?.model !== "detailed") return [chunk];
|
|
12
|
+
if (chunk.type === "text-start") return [chunk, { type: "text-delta", id: chunk.id, delta: "Detailed fixture response: " }];
|
|
13
|
+
if (chunk.type === "finish") return [{ ...chunk, messageMetadata: { ...chunk.messageMetadata, model: "fixture-detailed" } }];
|
|
14
|
+
return [chunk];
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
chosenChunks,
|
|
19
|
+
fixtureChoices
|
|
20
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AiChatClient } from "../services/types";
|
|
2
|
+
import { type ChatTransport } from "ai";
|
|
3
|
+
import { CHAT_SCENARIOS, type ChatScenario } from "./fixture-script";
|
|
4
|
+
import { type AiChatMessage } from "../services/types";
|
|
5
|
+
export { CHAT_SCENARIOS };
|
|
6
|
+
export type { ChatScenario };
|
|
7
|
+
/**
|
|
8
|
+
* A chat backend that answers in the browser, so a preview or a test runs the AI SDK's own
|
|
9
|
+
* `Chat` and the real parts with no route and no key.
|
|
10
|
+
*
|
|
11
|
+
* It is a `ChatTransport` — the SDK's own seam for "something else answers" — rather than a
|
|
12
|
+
* mock of anything above it: the chat state, the stop, the part states and the error branch
|
|
13
|
+
* are the SDK's, and the chunks are the ones a route streams. Hand both to a chat:
|
|
14
|
+
* `new Chat({ transport, messages })`. → docs/concepts/fixtures.md
|
|
15
|
+
*/
|
|
16
|
+
export declare function createAiChatFixture(scenario?: ChatScenario): {
|
|
17
|
+
transport: ChatTransport<AiChatMessage>;
|
|
18
|
+
messages: AiChatMessage[];
|
|
19
|
+
client: AiChatClient;
|
|
20
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { fixtureChoices, chosenChunks } from "./fixture-selection.js";
|
|
2
|
+
import { questionTurn } from "./fixture-question.js";
|
|
3
|
+
import { approvalTurn } from "./fixture-approval.js";
|
|
4
|
+
import { APICallError } from "ai";
|
|
5
|
+
import { forgetMissingConfiguration } from "@slices.sh/core/configuration";
|
|
6
|
+
import { CHAT_SCENARIOS, scriptFor, turnOf } from "./fixture-script.js";
|
|
7
|
+
import { refusalText, statusForChatError } from "../services/types.js";
|
|
8
|
+
function createAiChatFixture(scenario = "ready") {
|
|
9
|
+
const script = scriptFor(scenario);
|
|
10
|
+
forgetMissingConfiguration();
|
|
11
|
+
const transport = {
|
|
12
|
+
async sendMessages({ messages, abortSignal, body }) {
|
|
13
|
+
await wait(script.pace * 4, abortSignal);
|
|
14
|
+
if (script.refuse) throw refused(script.refuse);
|
|
15
|
+
return paced(
|
|
16
|
+
scenario === "questions" ? questionTurn(messages) : scenario === "approval" ? approvalTurn(messages) : chosenChunks(turnOf(messages, script.rich), body?.selection),
|
|
17
|
+
script.pace,
|
|
18
|
+
abortSignal
|
|
19
|
+
);
|
|
20
|
+
},
|
|
21
|
+
// Nothing outlives a page here, so there is never a stream to go back to.
|
|
22
|
+
async reconnectToStream() {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const client = {
|
|
27
|
+
transportFor: () => transport,
|
|
28
|
+
choicesFor: async () => scenario === "rich" ? fixtureChoices : null
|
|
29
|
+
};
|
|
30
|
+
return { transport, messages: script.history, client };
|
|
31
|
+
}
|
|
32
|
+
function refused(error) {
|
|
33
|
+
const typed = error instanceof Error;
|
|
34
|
+
const body = typed ? refusalText(error) : JSON.stringify(error);
|
|
35
|
+
return new APICallError({
|
|
36
|
+
message: body,
|
|
37
|
+
url: "fixture",
|
|
38
|
+
requestBodyValues: void 0,
|
|
39
|
+
statusCode: typed ? statusForChatError[error.slug] : 503,
|
|
40
|
+
responseBody: body
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function paced(chunks, pace, signal) {
|
|
44
|
+
let next = 0;
|
|
45
|
+
return new ReadableStream({
|
|
46
|
+
async pull(controller) {
|
|
47
|
+
const chunk = chunks[next++];
|
|
48
|
+
if (!chunk) return controller.close();
|
|
49
|
+
try {
|
|
50
|
+
await wait(chunk.type.endsWith("-delta") ? pace : pace * 3, signal);
|
|
51
|
+
} catch {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
controller.enqueue(chunk);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function wait(ms, signal) {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
if (signal?.aborted) return reject(signal.reason);
|
|
61
|
+
const timer = setTimeout(() => {
|
|
62
|
+
signal?.removeEventListener("abort", onAbort);
|
|
63
|
+
resolve();
|
|
64
|
+
}, ms);
|
|
65
|
+
function onAbort() {
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
reject(signal?.reason);
|
|
68
|
+
}
|
|
69
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export {
|
|
73
|
+
CHAT_SCENARIOS,
|
|
74
|
+
createAiChatFixture
|
|
75
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AiChatClient } from "../services/types";
|
|
2
|
+
import { type ChatScenario } from "./fixture";
|
|
3
|
+
export type FixtureOptions = {
|
|
4
|
+
/**
|
|
5
|
+
* ready, approval, questions, empty, rich, artifacts, slow, interrupted, failed, rate-limited,
|
|
6
|
+
* quota-exceeded, unknown-chat or missing-configuration. Default "ready".
|
|
7
|
+
*/
|
|
8
|
+
scenario?: ChatScenario;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* A ChatTransport that emits scripted UI message chunks in the browser, for previews and tests.
|
|
12
|
+
* The parts above it are the shipped ones. No key, no route, no server half:
|
|
13
|
+
* `aiChat({ provider: fixture(), model: "fixture" })`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function fixture(options?: FixtureOptions): import("@slices.sh/core").Provider<AiChatClient>;
|