tui: optimistically render submitted prompts
This commit is contained in:
parent
0ba1081cf1
commit
f5c770d65e
2 changed files with 101 additions and 10 deletions
|
|
@ -1178,6 +1178,23 @@ export function Prompt(props: PromptProps) {
|
||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
const parts = [
|
||||||
|
...editorParts,
|
||||||
|
{
|
||||||
|
id: PartID.ascending(),
|
||||||
|
type: "text" as const,
|
||||||
|
text: inputText,
|
||||||
|
},
|
||||||
|
...nonTextParts.map(assign),
|
||||||
|
]
|
||||||
|
sync.session.addOptimisticPrompt({
|
||||||
|
sessionID,
|
||||||
|
messageID,
|
||||||
|
agent: agent.name,
|
||||||
|
model: selectedModel,
|
||||||
|
variant,
|
||||||
|
parts,
|
||||||
|
})
|
||||||
sdk.client.session
|
sdk.client.session
|
||||||
.prompt({
|
.prompt({
|
||||||
sessionID,
|
sessionID,
|
||||||
|
|
@ -1186,17 +1203,9 @@ export function Prompt(props: PromptProps) {
|
||||||
agent: agent.name,
|
agent: agent.name,
|
||||||
model: selectedModel,
|
model: selectedModel,
|
||||||
variant,
|
variant,
|
||||||
parts: [
|
parts,
|
||||||
...editorParts,
|
|
||||||
{
|
|
||||||
id: PartID.ascending(),
|
|
||||||
type: "text",
|
|
||||||
text: inputText,
|
|
||||||
},
|
|
||||||
...nonTextParts.map(assign),
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
.catch(() => {})
|
.catch(() => sync.session.removeOptimisticPrompt(sessionID, messageID))
|
||||||
if (editorParts.length > 0) editor.markSelectionSent()
|
if (editorParts.length > 0) editor.markSelectionSent()
|
||||||
}
|
}
|
||||||
history.append({
|
history.append({
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@ import type {
|
||||||
Provider,
|
Provider,
|
||||||
Session,
|
Session,
|
||||||
Part,
|
Part,
|
||||||
|
TextPartInput,
|
||||||
|
FilePartInput,
|
||||||
|
AgentPartInput,
|
||||||
|
SubtaskPartInput,
|
||||||
Config,
|
Config,
|
||||||
Todo,
|
Todo,
|
||||||
Command,
|
Command,
|
||||||
|
|
@ -34,6 +38,8 @@ import path from "path"
|
||||||
import { useKV } from "./kv"
|
import { useKV } from "./kv"
|
||||||
import { aggregateFailures } from "./aggregate-failures"
|
import { aggregateFailures } from "./aggregate-failures"
|
||||||
|
|
||||||
|
type OptimisticPromptPart = (TextPartInput | FilePartInput | AgentPartInput | SubtaskPartInput) & { id: string }
|
||||||
|
|
||||||
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
name: "Sync",
|
name: "Sync",
|
||||||
init: () => {
|
init: () => {
|
||||||
|
|
@ -518,6 +524,82 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
||||||
if (last.role === "user") return "working"
|
if (last.role === "user") return "working"
|
||||||
return last.time.completed ? "idle" : "working"
|
return last.time.completed ? "idle" : "working"
|
||||||
},
|
},
|
||||||
|
addOptimisticPrompt(input: {
|
||||||
|
sessionID: string
|
||||||
|
messageID: string
|
||||||
|
agent: string
|
||||||
|
model: { providerID: string; modelID: string }
|
||||||
|
variant?: string
|
||||||
|
parts: OptimisticPromptPart[]
|
||||||
|
}) {
|
||||||
|
const messages = store.message[input.sessionID]
|
||||||
|
const match = messages ? Binary.search(messages, input.messageID, (m) => m.id) : undefined
|
||||||
|
const info: Message = {
|
||||||
|
id: input.messageID,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
role: "user",
|
||||||
|
time: { created: Date.now() },
|
||||||
|
agent: input.agent,
|
||||||
|
model: {
|
||||||
|
providerID: input.model.providerID,
|
||||||
|
modelID: input.model.modelID,
|
||||||
|
...(input.variant ? { variant: input.variant } : {}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const parts = input.parts.map((part): Part => {
|
||||||
|
const withIDs = {
|
||||||
|
...part,
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
messageID: input.messageID,
|
||||||
|
}
|
||||||
|
if (withIDs.type !== "text") return withIDs
|
||||||
|
return {
|
||||||
|
...withIDs,
|
||||||
|
metadata: {
|
||||||
|
...withIDs.metadata,
|
||||||
|
optimistic: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
batch(() => {
|
||||||
|
if (!messages) {
|
||||||
|
setStore("message", input.sessionID, [info])
|
||||||
|
} else if (!match?.found) {
|
||||||
|
setStore(
|
||||||
|
"message",
|
||||||
|
input.sessionID,
|
||||||
|
produce((draft) => {
|
||||||
|
draft.splice(match?.index ?? draft.length, 0, info)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
setStore("part", input.messageID, reconcile(parts))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeOptimisticPrompt(sessionID: string, messageID: string) {
|
||||||
|
if (!store.part[messageID]?.some((part) => part.type === "text" && part.metadata?.optimistic === true)) return
|
||||||
|
const messages = store.message[sessionID]
|
||||||
|
if (!messages) return
|
||||||
|
const match = Binary.search(messages, messageID, (m) => m.id)
|
||||||
|
batch(() => {
|
||||||
|
if (match.found) {
|
||||||
|
setStore(
|
||||||
|
"message",
|
||||||
|
sessionID,
|
||||||
|
produce((draft) => {
|
||||||
|
draft.splice(match.index, 1)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
setStore(
|
||||||
|
"part",
|
||||||
|
produce((draft) => {
|
||||||
|
delete draft[messageID]
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
},
|
||||||
async sync(sessionID: string) {
|
async sync(sessionID: string) {
|
||||||
if (fullSyncedSessions.has(sessionID)) return
|
if (fullSyncedSessions.has(sessionID)) return
|
||||||
const [session, messages, todo, diff] = await Promise.all([
|
const [session, messages, todo, diff] = await Promise.all([
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue