@datalayer/agent-runtimes 1.3.19 → 1.3.20
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/lib/loop/apps/AppRenderer.d.ts +9 -0
- package/lib/loop/apps/AppRenderer.js +28 -1
- package/lib/loop/embed/LoopEmbed.js +4 -0
- package/lib/loop/plugins/agents/DatalayerAgentBridge.js +14 -4
- package/lib/loop/plugins/agents/plugin.d.ts +12 -0
- package/lib/loop/plugins/agents/plugin.js +4 -2
- package/lib/loop/presets.d.ts +6 -0
- package/lib/loop/presets.js +4 -1
- package/package.json +1 -1
|
@@ -20,5 +20,14 @@ export type AppRendererProps = Omit<LoopEmbedProps, 'agentId'> & {
|
|
|
20
20
|
/** The application to render. */
|
|
21
21
|
app: AppSpec;
|
|
22
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* An application in a LOOP workspace: its agent, its starters, its layout.
|
|
25
|
+
*
|
|
26
|
+
* Every other prop is `LoopEmbed`'s, and wins over what the application
|
|
27
|
+
* implies — a host that wants the editors beside a chat still says so.
|
|
28
|
+
*/
|
|
29
|
+
/** No host plugins: one array, so that it never reads as a change. */
|
|
30
|
+
/** The agentspec a runtime is allocated with before an application's agent is created on it. */
|
|
31
|
+
export declare const DATALAYER_BOOTSTRAP_AGENTSPEC = "example-simple";
|
|
23
32
|
export declare function AppRenderer({ app, plugins, ...embed }: AppRendererProps): React.JSX.Element;
|
|
24
33
|
export default AppRenderer;
|
|
@@ -73,6 +73,8 @@ export function defineAppPlugin(app) {
|
|
|
73
73
|
* implies — a host that wants the editors beside a chat still says so.
|
|
74
74
|
*/
|
|
75
75
|
/** No host plugins: one array, so that it never reads as a change. */
|
|
76
|
+
/** The agentspec a runtime is allocated with before an application's agent is created on it. */
|
|
77
|
+
export const DATALAYER_BOOTSTRAP_AGENTSPEC = 'example-simple';
|
|
76
78
|
const NO_PLUGINS = [];
|
|
77
79
|
export function AppRenderer({ app, plugins = NO_PLUGINS, ...embed }) {
|
|
78
80
|
const chatOnly = app.interface.layout === 'chat';
|
|
@@ -87,6 +89,28 @@ export function AppRenderer({ app, plugins = NO_PLUGINS, ...embed }) {
|
|
|
87
89
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
88
90
|
[source]);
|
|
89
91
|
const allPlugins = useMemo(() => (appPlugin ? [appPlugin, ...plugins] : plugins), [appPlugin, plugins]);
|
|
92
|
+
/*
|
|
93
|
+
* On Datalayer, the application runs on a runtime: allocated with a plain
|
|
94
|
+
* agentspec, its agent created there with the application's spec — so that
|
|
95
|
+
* the runtime registers it and decides every tool call by its rules (LOOP
|
|
96
|
+
* R-03, R-05), as `loop apps run --cloud` does.
|
|
97
|
+
*/
|
|
98
|
+
const datalayerCreatePayload = useMemo(() => app.agent
|
|
99
|
+
? {
|
|
100
|
+
// Under the application's id, over AG-UI: what the workspace's
|
|
101
|
+
// chat addresses (`agentId` below).
|
|
102
|
+
name: app.id,
|
|
103
|
+
transport: 'ag-ui',
|
|
104
|
+
agent_spec_id: agentIdOf(app),
|
|
105
|
+
app_spec: dumpAppspec(app),
|
|
106
|
+
// Code Mode calls every tool through `execute_code`, which an
|
|
107
|
+
// application without a shell is refused: its tools are called
|
|
108
|
+
// one by one instead, each decided by its rules.
|
|
109
|
+
enable_codemode: Boolean(app.permissions?.computer?.shell),
|
|
110
|
+
}
|
|
111
|
+
: undefined,
|
|
112
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
113
|
+
[source]);
|
|
90
114
|
if (!appPlugin) {
|
|
91
115
|
return (_jsxs("div", { role: "status", style: { padding: 16 }, children: ["\u201C", app.name, "\u201D is run by a team, which the workspace does not run as an application yet."] }));
|
|
92
116
|
}
|
|
@@ -96,6 +120,9 @@ export function AppRenderer({ app, plugins = NO_PLUGINS, ...embed }) {
|
|
|
96
120
|
, {
|
|
97
121
|
// The application's own agent: created under its id, so that its
|
|
98
122
|
// spec is applied to an agent of its own, never to the one it extends.
|
|
99
|
-
agentId: app.id, editors: !chatOnly, showViewSelector: !chatOnly, teamPicker: false, showAgentVariants: false, graph: false, pluginsPanel: false,
|
|
123
|
+
agentId: app.id, editors: !chatOnly, showViewSelector: !chatOnly, teamPicker: false, showAgentVariants: false, graph: false, pluginsPanel: false, datalayerAgentSpecId: DATALAYER_BOOTSTRAP_AGENTSPEC,
|
|
124
|
+
// Where an application runs is decided by its host — the Studio's
|
|
125
|
+
// Preview, the hosted page — not offered to its user.
|
|
126
|
+
targetFixed: true, datalayerCreatePayload: datalayerCreatePayload, ...embed, plugins: allPlugins }));
|
|
100
127
|
}
|
|
101
128
|
export default AppRenderer;
|
|
@@ -63,6 +63,10 @@ export function LoopEmbed({ agentId = '', showHeader = false, chatHeaderActions,
|
|
|
63
63
|
[
|
|
64
64
|
preset.serverUrl,
|
|
65
65
|
preset.target,
|
|
66
|
+
preset.datalayerAgentSpecId,
|
|
67
|
+
preset.targetFixed,
|
|
68
|
+
// By its content: an application's spec written inline is a new object each render.
|
|
69
|
+
JSON.stringify(preset.datalayerCreatePayload ?? null),
|
|
66
70
|
preset.defaultEditor,
|
|
67
71
|
preset.showViewSelector,
|
|
68
72
|
preset.hideChatHeader,
|
|
@@ -19,8 +19,9 @@ import { useOptionalSandboxService } from './useSandboxService';
|
|
|
19
19
|
const DEFAULT_DATALAYER_AGENTSPEC = 'jupyter-tutor';
|
|
20
20
|
export function DatalayerAgentBridge() {
|
|
21
21
|
const reactor = useReactorPlatform();
|
|
22
|
-
const
|
|
23
|
-
|
|
22
|
+
const agentsConfig = reactor.getConfig(AGENTS_PLUGIN_NAME);
|
|
23
|
+
const agentSpecId = agentsConfig?.datalayerAgentSpecId ?? DEFAULT_DATALAYER_AGENTSPEC;
|
|
24
|
+
const createPayload = agentsConfig?.datalayerCreatePayload;
|
|
24
25
|
const service = useOptionalSandboxService();
|
|
25
26
|
const target = useSignalValue(service?.target ?? IDLE_SANDBOX_TARGET_SIGNAL);
|
|
26
27
|
const onDatalayer = target === 'datalayer';
|
|
@@ -45,12 +46,21 @@ export function DatalayerAgentBridge() {
|
|
|
45
46
|
* spec id in it and hands that over. Same thing, said in the place the
|
|
46
47
|
* creation actually looks.
|
|
47
48
|
*/
|
|
49
|
+
// An application names its own agent and transport; a bare spec, the spec's.
|
|
50
|
+
const agentName = typeof createPayload?.name === 'string' ? createPayload.name : agentSpecId;
|
|
51
|
+
const protocol = createPayload?.transport === 'ag-ui' ||
|
|
52
|
+
createPayload?.transport === 'vercel-ai'
|
|
53
|
+
? createPayload.transport
|
|
54
|
+
: undefined;
|
|
48
55
|
const agentConfig = useMemo(() => ({
|
|
49
|
-
name:
|
|
56
|
+
name: agentName,
|
|
57
|
+
...(protocol ? { protocol } : {}),
|
|
50
58
|
agentSpecId,
|
|
51
59
|
model: AGENTSPECS[agentSpecId]?.model,
|
|
52
60
|
description: AGENTSPECS[agentSpecId]?.description,
|
|
53
|
-
|
|
61
|
+
// An application's spec, when the workspace runs one.
|
|
62
|
+
...(createPayload ? { createPayload } : {}),
|
|
63
|
+
}), [agentSpecId, agentName, protocol, createPayload]);
|
|
54
64
|
const { runtime, status, error } = useAgentRuntimes({
|
|
55
65
|
agentSpecId,
|
|
56
66
|
agentConfig,
|
|
@@ -73,6 +73,18 @@ export type AgentsConfig = {
|
|
|
73
73
|
* The other targets take their agent from wherever they already run one.
|
|
74
74
|
*/
|
|
75
75
|
datalayerAgentSpecId?: string;
|
|
76
|
+
/**
|
|
77
|
+
* What the agent created on that runtime is created with besides its spec:
|
|
78
|
+
* an application's `app_spec`, so that the runtime runs the application and
|
|
79
|
+
* decides its tool calls by its rules (LOOP R-05, R-03).
|
|
80
|
+
*/
|
|
81
|
+
datalayerCreatePayload?: Record<string, unknown>;
|
|
82
|
+
/**
|
|
83
|
+
* The target is the host's decision, not the reader's: started there even
|
|
84
|
+
* with the control hidden. For an application that runs where it is
|
|
85
|
+
* deployed; a workspace leaves it unset.
|
|
86
|
+
*/
|
|
87
|
+
targetFixed?: boolean;
|
|
76
88
|
};
|
|
77
89
|
export type AgentsOutput = {
|
|
78
90
|
sandbox: SwitchableSandboxService;
|
|
@@ -57,8 +57,10 @@ export const AgentsPlugin = definePlugin({
|
|
|
57
57
|
sandbox: createSwitchableSandboxService({
|
|
58
58
|
serverUrl: config.serverUrl,
|
|
59
59
|
// Pinned when there is no choice to make: a host that hid the control
|
|
60
|
-
// must not be started on a target the reader cannot move off
|
|
61
|
-
|
|
60
|
+
// must not be started on a target the reader cannot move off — unless
|
|
61
|
+
// the host fixed the target itself, as an application deployed on
|
|
62
|
+
// Datalayer is (AppRenderer): there is nothing for the reader to move.
|
|
63
|
+
initialTarget: showVariants || config.targetFixed ? config.target : 'browser',
|
|
62
64
|
kernelSource: config.kernelSource,
|
|
63
65
|
localAgent: config.localAgent,
|
|
64
66
|
// The capacity plugins' say, read at switch time — lazily, because a
|
package/lib/loop/presets.d.ts
CHANGED
|
@@ -120,6 +120,12 @@ export type LoopPresetOptions = {
|
|
|
120
120
|
teamId?: string;
|
|
121
121
|
/** What a local agent is created from, when one is. */
|
|
122
122
|
localAgent?: AgentsConfig['localAgent'];
|
|
123
|
+
/** The agentspec a Datalayer runtime is allocated with (the `datalayer` target). */
|
|
124
|
+
datalayerAgentSpecId?: string;
|
|
125
|
+
/** What the agent on that runtime is created with besides its spec — an `app_spec`. */
|
|
126
|
+
datalayerCreatePayload?: Record<string, unknown>;
|
|
127
|
+
/** The target is the host's to fix, even with the agent control hidden. */
|
|
128
|
+
targetFixed?: boolean;
|
|
123
129
|
/**
|
|
124
130
|
* The agentspec the local agent is created from, by id.
|
|
125
131
|
*
|
package/lib/loop/presets.js
CHANGED
|
@@ -52,7 +52,7 @@ import { LoopPageLayoutPlugin } from './plugins/page-layout';
|
|
|
52
52
|
* be installed or signed into, and the switches a demonstration wants are off.
|
|
53
53
|
*/
|
|
54
54
|
export function loopPlugins(options = {}) {
|
|
55
|
-
const { serverUrl, target = 'browser', defaultEditor = 'notebook', showViewSelector = true, hideChatHeader = false, chatHeaderButtons = false, promptPlacement, autoFocusPrompt = true, suggestionLabels = true, fullScreenTopOffset = 0, showAgentVariants = false, agentSummary = true, teamId, teamPicker = true, pageLayout = false, pageLayoutTurnPanel = 'below', pageLayoutPrompt = 'docked', pageLayoutPromptAnchor = 'top', pageLayoutSize, pageLayoutTurnPanelFooter = 'full', localAgent, localAgentSpec, floatingPrompt = false, editorSelector = false, editors = true, graph = false, commandPalette = false, pluginsPanel = false, windowFrame = false, firstPromptHook, } = options;
|
|
55
|
+
const { serverUrl, target = 'browser', defaultEditor = 'notebook', showViewSelector = true, hideChatHeader = false, chatHeaderButtons = false, promptPlacement, autoFocusPrompt = true, suggestionLabels = true, fullScreenTopOffset = 0, showAgentVariants = false, agentSummary = true, teamId, teamPicker = true, pageLayout = false, pageLayoutTurnPanel = 'below', pageLayoutPrompt = 'docked', pageLayoutPromptAnchor = 'top', pageLayoutSize, pageLayoutTurnPanelFooter = 'full', localAgent, localAgentSpec, datalayerAgentSpecId, datalayerCreatePayload, targetFixed, floatingPrompt = false, editorSelector = false, editors = true, graph = false, commandPalette = false, pluginsPanel = false, windowFrame = false, firstPromptHook, } = options;
|
|
56
56
|
return [
|
|
57
57
|
// The chat owns the editor beside it, so which one opens is its
|
|
58
58
|
// configuration rather than the workspace's.
|
|
@@ -88,6 +88,9 @@ export function loopPlugins(options = {}) {
|
|
|
88
88
|
configurePlugin(AgentsPlugin, {
|
|
89
89
|
serverUrl,
|
|
90
90
|
target,
|
|
91
|
+
datalayerAgentSpecId,
|
|
92
|
+
datalayerCreatePayload,
|
|
93
|
+
targetFixed,
|
|
91
94
|
showAgentVariants,
|
|
92
95
|
showAgentSummary: agentSummary,
|
|
93
96
|
teamId,
|