@datalayer/agent-runtimes 1.3.28 → 1.3.30
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/AgentNode.js +14 -33
- package/lib/chat/ChatFloating.d.ts +12 -2
- package/lib/chat/ChatFloating.js +196 -25
- package/lib/chat/assistant/AssistantStage.d.ts +70 -0
- package/lib/chat/assistant/AssistantStage.js +192 -0
- package/lib/chat/assistant/SpeechBalloon.d.ts +26 -0
- package/lib/chat/assistant/SpeechBalloon.js +46 -0
- package/lib/chat/assistant/SpriteCharacter.d.ts +26 -0
- package/lib/chat/assistant/SpriteCharacter.js +73 -0
- package/lib/chat/assistant/characters.d.ts +30 -0
- package/lib/chat/assistant/characters.js +41 -0
- package/lib/chat/assistant/formats/acs.d.ts +88 -0
- package/lib/chat/assistant/formats/acs.js +493 -0
- package/lib/chat/assistant/formats/agentCompression.d.ts +5 -0
- package/lib/chat/assistant/formats/agentCompression.js +92 -0
- package/lib/chat/assistant/formats/blobs.d.ts +6 -0
- package/lib/chat/assistant/formats/blobs.js +52 -0
- package/lib/chat/assistant/formats/clippy.d.ts +21 -0
- package/lib/chat/assistant/formats/clippy.js +167 -0
- package/lib/chat/assistant/formats/index.d.ts +13 -0
- package/lib/chat/assistant/formats/index.js +15 -0
- package/lib/chat/assistant/formats/objectLiteral.d.ts +23 -0
- package/lib/chat/assistant/formats/objectLiteral.js +233 -0
- package/lib/chat/assistant/formats/stateAnimations.d.ts +16 -0
- package/lib/chat/assistant/formats/stateAnimations.js +115 -0
- package/lib/chat/assistant/formats/types.d.ts +56 -0
- package/lib/chat/assistant/formats/types.js +11 -0
- package/lib/chat/assistant/state.d.ts +62 -0
- package/lib/chat/assistant/state.js +116 -0
- package/lib/chat/base/ChatBase.js +41 -32
- package/lib/chat/base/modelChoice.d.ts +13 -0
- package/lib/chat/base/modelChoice.js +13 -0
- package/lib/chat/header/ChatViewModeToggle.js +6 -3
- package/lib/{loop/plugins/chat → chat/presence}/Presence.js +1 -1
- package/lib/{loop/plugins/chat → chat/presence}/presenceStatus.d.ts +7 -1
- package/lib/{loop/plugins/chat → chat/presence}/presenceStatus.js +18 -0
- package/lib/chat/viewModes.d.ts +6 -0
- package/lib/chat/viewModes.js +10 -1
- package/lib/examples/ChatAssistantExample.d.ts +8 -0
- package/lib/examples/ChatAssistantExample.js +59 -0
- package/lib/examples/example-selector.js +1 -0
- package/lib/examples/main.js +3 -0
- package/lib/loop/apps/AppRenderer.d.ts +34 -2
- package/lib/loop/apps/AppRenderer.js +96 -29
- package/lib/loop/apps/agent.d.ts +15 -0
- package/lib/loop/apps/agent.js +42 -0
- package/lib/loop/apps/appspec.d.ts +5 -1
- package/lib/loop/apps/appspec.js +21 -1
- package/lib/loop/apps/checks.js +3 -1
- package/lib/loop/apps/index.d.ts +1 -0
- package/lib/loop/apps/index.js +1 -0
- package/lib/loop/core/index.d.ts +23 -0
- package/lib/loop/core/index.js +1 -0
- package/lib/loop/embed/AppEmbed.d.ts +42 -0
- package/lib/loop/embed/AppEmbed.js +205 -0
- package/lib/loop/embed/datalayer-app.d.ts +1 -0
- package/lib/loop/embed/datalayer-app.js +16 -0
- package/lib/loop/embed/element.d.ts +57 -0
- package/lib/loop/embed/element.js +405 -0
- package/lib/loop/embed/embedConfig.d.ts +120 -0
- package/lib/loop/embed/embedConfig.js +148 -0
- package/lib/loop/embed/embedTheme.d.ts +24 -0
- package/lib/loop/embed/embedTheme.js +83 -0
- package/lib/loop/index.d.ts +6 -2
- package/lib/loop/index.js +8 -2
- package/lib/loop/plugins/a2ui-surface/InlineSurface.d.ts +21 -3
- package/lib/loop/plugins/a2ui-surface/InlineSurface.js +34 -6
- package/lib/loop/plugins/agent-capacity/index.d.ts +4 -0
- package/lib/loop/plugins/agent-capacity/index.js +3 -1
- package/lib/loop/plugins/agents/switchable.d.ts +7 -0
- package/lib/loop/plugins/agents/switchable.js +91 -88
- package/lib/loop/plugins/app-page/AppPage.d.ts +16 -0
- package/lib/loop/plugins/app-page/AppPage.js +53 -0
- package/lib/loop/plugins/app-page/appPage.d.ts +180 -0
- package/lib/loop/plugins/app-page/appPage.js +443 -0
- package/lib/loop/plugins/app-page/index.d.ts +8 -0
- package/lib/loop/plugins/app-page/index.js +34 -0
- package/lib/loop/plugins/assistant-characters/index.d.ts +30 -0
- package/lib/loop/plugins/assistant-characters/index.js +41 -0
- package/lib/loop/plugins/chat/ChatView.js +50 -39
- package/lib/loop/plugins/chat/index.d.ts +1 -1
- package/lib/loop/plugins/page-layout/SplitLayout.d.ts +31 -0
- package/lib/loop/plugins/page-layout/SplitLayout.js +129 -0
- package/lib/loop/plugins/page-layout/index.d.ts +13 -0
- package/lib/loop/plugins/page-layout/index.js +15 -2
- package/lib/loop/presets.d.ts +12 -0
- package/lib/loop/presets.js +5 -3
- package/lib/specs/actions.js +14 -0
- package/lib/specs/agents/agents.js +4 -0
- package/lib/specs/apps.d.ts +13 -1
- package/lib/specs/apps.js +2715 -373
- package/lib/specs/appspecSchema.js +21 -3
- package/lib/types/agentspecs.d.ts +21 -1
- package/lib/types/chat.d.ts +1 -1
- package/lib/types/config.d.ts +12 -0
- package/package.json +2 -1
- package/scripts/codegen/generate_agents.py +50 -2
- package/scripts/codegen/generate_apps.py +36 -6
- /package/lib/{loop/plugins/chat → chat/presence}/Presence.d.ts +0 -0
|
@@ -311,6 +311,18 @@ export const APPSPEC_SCHEMA = {
|
|
|
311
311
|
default: null,
|
|
312
312
|
description: 'The component tree, when there is one',
|
|
313
313
|
},
|
|
314
|
+
assistant: {
|
|
315
|
+
anyOf: [
|
|
316
|
+
{
|
|
317
|
+
$ref: '#/$defs/AssistantCharacter',
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
type: 'null',
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
default: null,
|
|
324
|
+
description: 'The character its floating assistant shows: `paperclip`, `wizard`, `cat` or `eyes`. The paper clip when unsaid; a person may choose another in their settings',
|
|
325
|
+
},
|
|
314
326
|
},
|
|
315
327
|
title: 'AppInterface',
|
|
316
328
|
type: 'object',
|
|
@@ -553,7 +565,7 @@ export const APPSPEC_SCHEMA = {
|
|
|
553
565
|
},
|
|
554
566
|
composed_by: {
|
|
555
567
|
default: '',
|
|
556
|
-
description: "Who composed it: a model's id, `developer`, `template`",
|
|
568
|
+
description: "Who composed it: a model's id, `canvas` (a person on the Canvas), `developer`, `template`",
|
|
557
569
|
title: 'Composed By',
|
|
558
570
|
type: 'string',
|
|
559
571
|
},
|
|
@@ -659,6 +671,12 @@ export const APPSPEC_SCHEMA = {
|
|
|
659
671
|
title: 'AppTrigger',
|
|
660
672
|
type: 'object',
|
|
661
673
|
},
|
|
674
|
+
AssistantCharacter: {
|
|
675
|
+
description: "The character an application's floating assistant shows (LOOP T-24):\none of those a UI plugin contributes. Datalayer's are these four.",
|
|
676
|
+
enum: ['paperclip', 'wizard', 'cat', 'eyes'],
|
|
677
|
+
title: 'AssistantCharacter',
|
|
678
|
+
type: 'string',
|
|
679
|
+
},
|
|
662
680
|
Behaviour: {
|
|
663
681
|
description: 'What an application does when it meets an action: the four a person chooses from.',
|
|
664
682
|
enum: ['do_it', 'if_asked', 'ask_first', 'leave_to_me'],
|
|
@@ -673,7 +691,7 @@ export const APPSPEC_SCHEMA = {
|
|
|
673
691
|
},
|
|
674
692
|
EmbedMode: {
|
|
675
693
|
description: "How an application sits in another product's page.",
|
|
676
|
-
enum: ['inline', 'bubble', 'panel'],
|
|
694
|
+
enum: ['inline', 'bubble', 'panel', 'assistant'],
|
|
677
695
|
title: 'EmbedMode',
|
|
678
696
|
type: 'string',
|
|
679
697
|
},
|
|
@@ -684,7 +702,7 @@ export const APPSPEC_SCHEMA = {
|
|
|
684
702
|
mode: {
|
|
685
703
|
$ref: '#/$defs/EmbedMode',
|
|
686
704
|
default: 'inline',
|
|
687
|
-
description: '`inline`, `bubble` or `
|
|
705
|
+
description: '`inline`, `bubble`, `panel` or `assistant`',
|
|
688
706
|
},
|
|
689
707
|
origins: {
|
|
690
708
|
description: 'The origins allowed to embed it',
|
|
@@ -372,11 +372,24 @@ export interface ServerActionsSpec {
|
|
|
372
372
|
}
|
|
373
373
|
/** What kind of application it is: what its user meets. */
|
|
374
374
|
export type AppKind = 'chat' | 'widget' | 'decision' | 'worker';
|
|
375
|
+
/**
|
|
376
|
+
* How an application was built: in Python (its `app.py`), on the Canvas (its
|
|
377
|
+
* page composed there) or written out as its spec. Carried beside the
|
|
378
|
+
* catalogue (`APP_BUILT`), since an Appspec does not hold it.
|
|
379
|
+
*/
|
|
380
|
+
export type AppBuilt = 'python' | 'canvas' | 'written';
|
|
375
381
|
/** What an application does when it meets an action. */
|
|
376
382
|
export type AppBehaviour = 'do_it' | 'if_asked' | 'ask_first' | 'leave_to_me';
|
|
377
383
|
export type AppLayout = 'chat' | 'page' | 'split';
|
|
378
384
|
/** The one colour of an application; everything else is neutral. */
|
|
379
385
|
export type AppAccent = 'green' | 'rose' | 'sky' | 'lime' | 'sun' | 'violet';
|
|
386
|
+
/**
|
|
387
|
+
* The character an application's floating assistant shows (LOOP T-24): one of
|
|
388
|
+
* those Datalayer's plugin contributes.
|
|
389
|
+
*/
|
|
390
|
+
export type AppAssistantCharacter = 'paperclip' | 'wizard' | 'cat' | 'eyes';
|
|
391
|
+
/** How an application sits in another product's page (LOOP D-07). */
|
|
392
|
+
export type AppEmbedMode = 'inline' | 'bubble' | 'panel' | 'assistant';
|
|
380
393
|
/** Something an application reaches. */
|
|
381
394
|
export interface AppConnectionSpec {
|
|
382
395
|
/** An MCP server, `id` or `id:version`. */
|
|
@@ -443,6 +456,8 @@ export interface AppInterfaceSpec {
|
|
|
443
456
|
/** The components of the catalog the surface may use. */
|
|
444
457
|
components: string[];
|
|
445
458
|
surface?: AppSurfaceSpec;
|
|
459
|
+
/** The character its floating assistant shows; the paper clip when unsaid. */
|
|
460
|
+
assistant?: AppAssistantCharacter;
|
|
446
461
|
}
|
|
447
462
|
export interface AppTestCaseSpec {
|
|
448
463
|
ask: string;
|
|
@@ -474,7 +489,7 @@ export interface AppDeploymentSpec {
|
|
|
474
489
|
slug: string;
|
|
475
490
|
};
|
|
476
491
|
embedded?: {
|
|
477
|
-
mode:
|
|
492
|
+
mode: AppEmbedMode;
|
|
478
493
|
origins: string[];
|
|
479
494
|
};
|
|
480
495
|
}
|
|
@@ -586,6 +601,11 @@ export interface Agentspec {
|
|
|
586
601
|
enabled: boolean;
|
|
587
602
|
/** AI model identifier to use for this agent */
|
|
588
603
|
model?: string;
|
|
604
|
+
/**
|
|
605
|
+
* Other models of the catalogue this agent may be switched to, beside its
|
|
606
|
+
* `model`. The runtime offers those of them its inference serves.
|
|
607
|
+
*/
|
|
608
|
+
modelAdditionals?: string[];
|
|
589
609
|
/** Inference provider routing strategy */
|
|
590
610
|
inferenceProvider?: 'local' | 'datalayer';
|
|
591
611
|
/** MCP servers used by this agent */
|
package/lib/types/chat.d.ts
CHANGED
|
@@ -66,7 +66,7 @@ export interface ToolCallCompleteContext {
|
|
|
66
66
|
* - 'floating-draggable': The popup with a handle, movable anywhere in the viewport
|
|
67
67
|
* - 'sidebar': Docked sidebar panel — offered only where the host has a mount point for it
|
|
68
68
|
*/
|
|
69
|
-
export type ChatViewMode = 'floating' | 'floating-small' | 'floating-draggable' | 'sidebar';
|
|
69
|
+
export type ChatViewMode = 'floating' | 'floating-small' | 'floating-draggable' | 'assistant' | 'sidebar';
|
|
70
70
|
/**
|
|
71
71
|
* Companion "ephemeral surface" shown next to the chat.
|
|
72
72
|
* - 'none': chat only, no companion surface
|
package/lib/types/config.d.ts
CHANGED
|
@@ -45,7 +45,19 @@ export interface AgentConfig {
|
|
|
45
45
|
* Remote configuration from server
|
|
46
46
|
*/
|
|
47
47
|
export interface RemoteConfig {
|
|
48
|
+
/**
|
|
49
|
+
* The models on offer — for an agent, its `model` and `model_additionals` —
|
|
50
|
+
* each saying whether it can be used.
|
|
51
|
+
*/
|
|
48
52
|
models: ModelConfig[];
|
|
53
|
+
/**
|
|
54
|
+
* Who decided which models can be used: ai-inference's own list, or the
|
|
55
|
+
* runtime's configuration when it did not route through ai-inference or
|
|
56
|
+
* ai-inference did not answer.
|
|
57
|
+
*/
|
|
58
|
+
modelsSource?: 'ai-inference' | 'local';
|
|
59
|
+
/** That decision in a sentence. */
|
|
60
|
+
modelsNote?: string;
|
|
49
61
|
defaultModel?: string;
|
|
50
62
|
builtinTools: BuiltinTool[];
|
|
51
63
|
mcpServers?: MCPServerConfig[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalayer/agent-runtimes",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.30",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
".",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"build": "npm run clean && gulp resources-to-lib && tsc -b && cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build",
|
|
57
57
|
"build-storybook": "storybook build",
|
|
58
58
|
"build:all": "npm run build && npm run build:examples",
|
|
59
|
+
"build:embed": "cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --config vite.embed.config.ts",
|
|
59
60
|
"build:examples": "npm run build:nextjs",
|
|
60
61
|
"build:lib": "npm run clean:lib && gulp resources-to-lib && tsc -b",
|
|
61
62
|
"build:nextjs": "npm run build --workspace=nextjs-notebook-example",
|
|
@@ -276,9 +276,44 @@ def load_yaml_specs(specs_dir: Path) -> List[tuple[str, Dict[str, Any]]]:
|
|
|
276
276
|
spec = resolve_spec(spec, by_id, fragments_by_id)
|
|
277
277
|
resolved.append((subfolder, spec))
|
|
278
278
|
|
|
279
|
+
check_model_additionals(resolved, load_model_ids(specs_dir))
|
|
279
280
|
return resolved
|
|
280
281
|
|
|
281
282
|
|
|
283
|
+
def load_model_ids(specs_dir: Path) -> set[str]:
|
|
284
|
+
"""Every id the models catalogue answers to: its ids and their aliases."""
|
|
285
|
+
ids: set[str] = set()
|
|
286
|
+
for yaml_file in sorted((specs_dir.parent / "models").glob("*.yaml")):
|
|
287
|
+
with open(yaml_file, "r") as f:
|
|
288
|
+
model = yaml.safe_load(f) or {}
|
|
289
|
+
if model.get("id"):
|
|
290
|
+
ids.add(str(model["id"]))
|
|
291
|
+
ids.update(str(alias) for alias in model.get("aliases") or [])
|
|
292
|
+
return ids
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def check_model_additionals(
|
|
296
|
+
specs: List[tuple[str, Dict[str, Any]]], model_ids: set[str]
|
|
297
|
+
) -> None:
|
|
298
|
+
"""Refuse an agent whose `model_additionals` names a model the catalogue does not know."""
|
|
299
|
+
problems = []
|
|
300
|
+
for _, spec in specs:
|
|
301
|
+
additionals = spec.get("model_additionals")
|
|
302
|
+
if additionals is None:
|
|
303
|
+
continue
|
|
304
|
+
if not isinstance(additionals, list):
|
|
305
|
+
problems.append(f"{spec.get('id')}: model_additionals is not a list")
|
|
306
|
+
continue
|
|
307
|
+
unknown = [str(m) for m in additionals if str(m) not in model_ids]
|
|
308
|
+
if unknown:
|
|
309
|
+
problems.append(
|
|
310
|
+
f"{spec.get('id')}: model_additionals names models the catalogue "
|
|
311
|
+
f"does not know: {', '.join(unknown)}"
|
|
312
|
+
)
|
|
313
|
+
if problems:
|
|
314
|
+
raise SystemExit("\n".join(problems))
|
|
315
|
+
|
|
316
|
+
|
|
282
317
|
def generate_python_code(specs: List[tuple[str, Dict[str, Any]]]) -> str:
|
|
283
318
|
"""Generate Python code from agent specifications."""
|
|
284
319
|
# Header
|
|
@@ -423,6 +458,12 @@ from agent_runtimes.types import (
|
|
|
423
458
|
# Model field
|
|
424
459
|
model_id = spec.get("model")
|
|
425
460
|
model_str = f'"{model_id}"' if model_id else "None"
|
|
461
|
+
model_additionals = spec.get("model_additionals") or []
|
|
462
|
+
model_additionals_py_line = (
|
|
463
|
+
f" model_additionals={_fmt_list(model_additionals)},\n"
|
|
464
|
+
if model_additionals
|
|
465
|
+
else ""
|
|
466
|
+
)
|
|
426
467
|
inference_provider = spec.get("inference_provider")
|
|
427
468
|
inference_provider_str = (
|
|
428
469
|
f'"{inference_provider}"' if inference_provider else "None"
|
|
@@ -547,7 +588,7 @@ from agent_runtimes.types import (
|
|
|
547
588
|
domain={f'"{domain_value}"' if domain_value else "None"},
|
|
548
589
|
enabled={spec.get("enabled", True)},
|
|
549
590
|
model={model_str},
|
|
550
|
-
inference_provider={inference_provider_str},
|
|
591
|
+
{model_additionals_py_line} inference_provider={inference_provider_str},
|
|
551
592
|
mcp_servers=[{mcp_servers_str}],
|
|
552
593
|
skills={_fmt_list(skill_refs)},
|
|
553
594
|
tools={_fmt_list(tool_refs)},
|
|
@@ -1045,6 +1086,12 @@ const FRONTEND_TOOL_MAP: Record<string, any> = {
|
|
|
1045
1086
|
# Model field
|
|
1046
1087
|
model_id = spec.get("model")
|
|
1047
1088
|
model_ts = f"'{model_id}'" if model_id else "undefined"
|
|
1089
|
+
model_additionals = spec.get("model_additionals") or []
|
|
1090
|
+
model_additionals_ts_line = (
|
|
1091
|
+
f" modelAdditionals: {_fmt_ts_literal(model_additionals)},\n"
|
|
1092
|
+
if model_additionals
|
|
1093
|
+
else ""
|
|
1094
|
+
)
|
|
1048
1095
|
inference_provider = spec.get("inference_provider")
|
|
1049
1096
|
inference_provider_ts = (
|
|
1050
1097
|
f"'{inference_provider}'" if inference_provider else "undefined"
|
|
@@ -1116,7 +1163,7 @@ const FRONTEND_TOOL_MAP: Record<string, any> = {
|
|
|
1116
1163
|
domain: {domain_ts},
|
|
1117
1164
|
enabled: {str(spec.get("enabled", True)).lower()},
|
|
1118
1165
|
model: {model_ts},
|
|
1119
|
-
{inference_provider_line} mcpServers: [{mcp_servers_str}],
|
|
1166
|
+
{model_additionals_ts_line}{inference_provider_line} mcpServers: [{mcp_servers_str}],
|
|
1120
1167
|
skills: [{skills_str}].filter(Boolean) as SkillSpec[],
|
|
1121
1168
|
tools: [{tools_str}],
|
|
1122
1169
|
{disable_tool_approvals_line} frontendTools: [{frontend_tools_str}],
|
|
@@ -1588,6 +1635,7 @@ export * from './tracks';
|
|
|
1588
1635
|
export * from './ops';
|
|
1589
1636
|
export * from './apps';
|
|
1590
1637
|
export * from './actions';
|
|
1638
|
+
export * from './appspecSchema';
|
|
1591
1639
|
"""
|
|
1592
1640
|
)
|
|
1593
1641
|
|
|
@@ -17,6 +17,11 @@ read, write, send, buy, delete, publish — read from ``agentspecs.actions``.
|
|
|
17
17
|
Each application is validated here, by ``agentspecs`` itself, and arrives
|
|
18
18
|
with its layout said and with what it names that is not enabled (`setup`).
|
|
19
19
|
|
|
20
|
+
How each was built travels beside it (``APP_BUILT``), since an Appspec does
|
|
21
|
+
not hold it: ``python`` when ``apps/<id>/app.py`` sits beside its spec
|
|
22
|
+
(agentspecs >= 0.0.15), ``canvas`` when its page says it was composed on the
|
|
23
|
+
Canvas (``composed_by: canvas``, agentspecs >= 0.0.23), else ``written``.
|
|
24
|
+
|
|
20
25
|
Usage:
|
|
21
26
|
python generate_apps.py \\
|
|
22
27
|
--specs-dir agentspecs/agentspecs/apps \\
|
|
@@ -57,6 +62,16 @@ def _flat(text: Any) -> str:
|
|
|
57
62
|
return " ".join(str(text or "").split())
|
|
58
63
|
|
|
59
64
|
|
|
65
|
+
def built_of(specs_dir: Path, identity: str, app: Any) -> str:
|
|
66
|
+
"""How an application was built: `python`, `canvas` or `written`."""
|
|
67
|
+
if (specs_dir / identity / "app.py").is_file():
|
|
68
|
+
return "python"
|
|
69
|
+
surface = app.interface.surface
|
|
70
|
+
if surface is not None and surface.composed_by == "canvas":
|
|
71
|
+
return "canvas"
|
|
72
|
+
return "written"
|
|
73
|
+
|
|
74
|
+
|
|
60
75
|
def load_specs(specs_dir: Path) -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
61
76
|
"""Every application, validated, as plain data — and the action classes."""
|
|
62
77
|
apps_module = import_from_clone(specs_dir, "apps")
|
|
@@ -66,6 +81,7 @@ def load_specs(specs_dir: Path) -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
|
66
81
|
behaviours: dict[str, dict[str, str]] = {}
|
|
67
82
|
escalations: dict[str, dict[str, Any]] = {}
|
|
68
83
|
sources: dict[str, Any] = {}
|
|
84
|
+
built: dict[str, str] = {}
|
|
69
85
|
for identity in sorted(apps):
|
|
70
86
|
app = apps[identity]
|
|
71
87
|
spec = app.model_dump(mode="json", by_alias=True)
|
|
@@ -78,6 +94,7 @@ def load_specs(specs_dir: Path) -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
|
78
94
|
}
|
|
79
95
|
escalations[identity] = dict(sorted(apps_module.tool_escalations(app).items()))
|
|
80
96
|
sources[identity] = apps_module.dump_app(app)
|
|
97
|
+
built[identity] = built_of(specs_dir, identity, app)
|
|
81
98
|
for rule in spec["rules"]:
|
|
82
99
|
rule["applies_to"] = (
|
|
83
100
|
[rule["applies_to"]]
|
|
@@ -129,6 +146,7 @@ def load_specs(specs_dir: Path) -> tuple[list[dict[str, Any]], dict[str, Any]]:
|
|
|
129
146
|
"behaviours": behaviours,
|
|
130
147
|
"escalations": escalations,
|
|
131
148
|
"sources": sources,
|
|
149
|
+
"built": built,
|
|
132
150
|
}
|
|
133
151
|
return specs, actions
|
|
134
152
|
|
|
@@ -174,7 +192,7 @@ ABOUT_ACTIONS = [
|
|
|
174
192
|
]
|
|
175
193
|
|
|
176
194
|
|
|
177
|
-
def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
195
|
+
def generate_python_code(specs: list[dict[str, Any]], built: dict[str, str]) -> str:
|
|
178
196
|
"""Generate the Python application catalogue."""
|
|
179
197
|
lines = [
|
|
180
198
|
"# Copyright (c) 2025-2026 Datalayer, Inc.",
|
|
@@ -185,7 +203,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
185
203
|
*HEADER,
|
|
186
204
|
'"""',
|
|
187
205
|
"",
|
|
188
|
-
"from typing import Dict",
|
|
206
|
+
"from typing import Dict, Literal",
|
|
189
207
|
"",
|
|
190
208
|
"from agent_runtimes.types import AppSpec",
|
|
191
209
|
"",
|
|
@@ -215,6 +233,10 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
215
233
|
[
|
|
216
234
|
"}",
|
|
217
235
|
"",
|
|
236
|
+
"#: How each application was built: `python` (its `app.py`), `canvas` (its",
|
|
237
|
+
"#: page composed on the Canvas) or `written` (its spec written out).",
|
|
238
|
+
f"APP_BUILT: Dict[str, Literal['python', 'canvas', 'written']] = {built!r}",
|
|
239
|
+
"",
|
|
218
240
|
"",
|
|
219
241
|
"def get_app(app_id: str) -> AppSpec | None:",
|
|
220
242
|
' """An application, by `id` or `id:version`, or None."""',
|
|
@@ -235,7 +257,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
235
257
|
|
|
236
258
|
|
|
237
259
|
def generate_typescript_code(
|
|
238
|
-
specs: list[dict[str, Any]], sources: dict[str, Any]
|
|
260
|
+
specs: list[dict[str, Any]], sources: dict[str, Any], built: dict[str, str]
|
|
239
261
|
) -> str:
|
|
240
262
|
"""Generate the TypeScript application catalogue."""
|
|
241
263
|
lines = [
|
|
@@ -250,7 +272,7 @@ def generate_typescript_code(
|
|
|
250
272
|
*[f" * {line}" for line in HEADER],
|
|
251
273
|
" */",
|
|
252
274
|
"",
|
|
253
|
-
"import type { AppKind, AppSpec } from '../types/agentspecs';",
|
|
275
|
+
"import type { AppBuilt, AppKind, AppSpec } from '../types/agentspecs';",
|
|
254
276
|
"",
|
|
255
277
|
]
|
|
256
278
|
for spec in specs:
|
|
@@ -288,6 +310,14 @@ def generate_typescript_code(
|
|
|
288
310
|
+ json.dumps(sources, indent=2, ensure_ascii=False)
|
|
289
311
|
+ ";",
|
|
290
312
|
"",
|
|
313
|
+
"/**",
|
|
314
|
+
" * How each application was built: `python` (its `app.py`), `canvas` (its",
|
|
315
|
+
" * page composed on the Canvas) or `written` (its spec written out).",
|
|
316
|
+
" */",
|
|
317
|
+
"export const APP_BUILT: Record<string, AppBuilt> = "
|
|
318
|
+
+ json.dumps(built, indent=2)
|
|
319
|
+
+ ";",
|
|
320
|
+
"",
|
|
291
321
|
"/** Every application of the catalogue, or those of a kind. */",
|
|
292
322
|
"export function listApps(kind?: AppKind): AppSpec[] {",
|
|
293
323
|
" return Object.values(APP_CATALOGUE).filter(",
|
|
@@ -475,10 +505,10 @@ def main() -> None:
|
|
|
475
505
|
sys.exit(1)
|
|
476
506
|
specs, actions = load_specs(args.specs_dir)
|
|
477
507
|
outputs = [
|
|
478
|
-
(args.python_output, generate_python_code(specs)),
|
|
508
|
+
(args.python_output, generate_python_code(specs, actions["built"])),
|
|
479
509
|
(
|
|
480
510
|
args.typescript_output,
|
|
481
|
-
generate_typescript_code(specs, actions["sources"]),
|
|
511
|
+
generate_typescript_code(specs, actions["sources"], actions["built"]),
|
|
482
512
|
),
|
|
483
513
|
(args.actions_python_output, generate_actions_python_code(actions)),
|
|
484
514
|
(args.actions_typescript_output, generate_actions_typescript_code(actions)),
|
|
File without changes
|