Assistant node, and the Control tab as one conversation (openpanel V5)

run_turn takes its tools, system prompt and tool caller; the chat passes the
MCP tools as before. The assistant node offers one tool per wired target,
reads its other inputs as context, streams its reply a sentence at a time,
warms the provider on `listening` and says so when the provider is not there.
Completions share the per-server HTTP client. wren replies when nothing reads
`unhandled`, stt shows a note when nothing was heard. /ai/control lists one
conversation per entry node with the messages to watch.
This commit is contained in:
Melvin Strobl
2026-10-19 23:23:25 +02:00
parent 4df7bdb057
commit a286cf4ceb
21 changed files with 937 additions and 184 deletions
+34 -5
View File
@@ -606,15 +606,39 @@ export const ControlNodeSchema = {
type: 'string',
title: 'Prompt Msg'
},
actions_msg: {
type: 'string',
title: 'Actions Msg'
watch: {
items: {
'$ref': '#/components/schemas/ControlWatch'
},
type: 'array',
title: 'Watch'
}
},
type: 'object',
required: ['id', 'flow', 'title', 'prompt_msg', 'actions_msg'],
required: ['id', 'flow', 'title', 'prompt_msg', 'watch'],
title: 'ControlNode',
description: 'A wren node the chat can talk to: where it sends and where it reads.'
description: 'A conversation the chat can hold: where a command goes, and what to show.'
} as const;
export const ControlWatchSchema = {
properties: {
msg: {
type: 'string',
title: 'Msg'
},
kind: {
'$ref': '#/components/schemas/WatchKind'
},
node: {
type: 'string',
title: 'Node',
default: ''
}
},
type: 'object',
required: ['msg', 'kind'],
title: 'ControlWatch',
description: 'A message the conversation shows, as one kind of line.'
} as const;
export const DTypeSchema = {
@@ -4937,6 +4961,11 @@ export const WaitingNodeSchema = {
title: 'WaitingNode'
} as const;
export const WatchKindSchema = {
type: 'string',
enum: ['partial', 'text', 'reply', 'actions']
} as const;
export const WebPushKeySchema = {
properties: {
key: {
+6 -3
View File
@@ -84,10 +84,13 @@ export class AiService {
/**
* Read Control Nodes
* The wren nodes of the running live flows, for the chat's Control tab.
* The conversations of the running live flows, for the chat's Control tab.
*
* Only those with both ends wired: the chat publishes the command on the
* prompt's message and reads the answer off the actions'.
* One per wren or assistant node a command reaches first. A node whose
* prompt is another's ``unhandled`` answers in that one's conversation,
* after it. The conversation shows the prompt, the words heard so far (the
* ``partial`` of a ``stt`` node writing the prompt), and each node's
* ``reply`` and ``actions``, so typed and spoken commands are one.
* @returns ControlNode Successful Response
* @throws ApiError
*/
+13 -2
View File
@@ -149,14 +149,23 @@ export type ConnectorStatePublic = {
};
/**
* A wren node the chat can talk to: where it sends and where it reads.
* A conversation the chat can hold: where a command goes, and what to show.
*/
export type ControlNode = {
id: string;
flow: string;
title: string;
prompt_msg: string;
actions_msg: string;
watch: Array<ControlWatch>;
};
/**
* A message the conversation shows, as one kind of line.
*/
export type ControlWatch = {
msg: string;
kind: WatchKind;
node?: string;
};
/**
@@ -1560,6 +1569,8 @@ export type WaitingNode = {
seconds: number;
};
export type WatchKind = 'partial' | 'text' | 'reply' | 'actions';
export type WebPushKey = {
key: string;
};