@datalayer/agent-runtimes 1.3.15 → 1.3.17
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/README.md +1 -1
- package/lib/chat/base/ChatBase.js +3 -2
- package/lib/config/AgentConfiguration.d.ts +1 -1
- package/lib/loop/apps/AppRenderer.d.ts +24 -0
- package/lib/loop/apps/AppRenderer.js +101 -0
- package/lib/loop/apps/appspec.d.ts +69 -0
- package/lib/loop/apps/appspec.js +566 -0
- package/lib/loop/apps/checks.d.ts +21 -0
- package/lib/loop/apps/checks.js +556 -0
- package/lib/loop/apps/index.d.ts +10 -0
- package/lib/loop/apps/index.js +14 -0
- package/lib/loop/apps/rules.d.ts +64 -0
- package/lib/loop/apps/rules.js +289 -0
- package/lib/loop/apps/yaml.d.ts +17 -0
- package/lib/loop/apps/yaml.js +185 -0
- package/lib/loop/embed/LoopEmbed.d.ts +6 -1
- package/lib/loop/embed/LoopEmbed.js +3 -2
- package/lib/loop/index.d.ts +1 -0
- package/lib/loop/index.js +3 -0
- package/lib/loop/plugins/chat/ChatView.js +9 -4
- package/lib/loop/plugins/chat/index.d.ts +10 -0
- package/lib/loop/plugins/chat/index.js +1 -0
- package/lib/loop/presets.d.ts +6 -0
- package/lib/loop/presets.js +2 -1
- package/lib/loop/shell/LoopWorkspace.d.ts +14 -0
- package/lib/loop/shell/LoopWorkspace.js +20 -6
- package/lib/loop/shell/index.d.ts +1 -1
- package/lib/loop/shell/index.js +1 -1
- package/lib/loop/shell/useWorkspaceFullScreen.d.ts +17 -4
- package/lib/loop/shell/useWorkspaceFullScreen.js +23 -6
- package/lib/specs/actions.d.ts +30 -0
- package/lib/specs/actions.js +616 -0
- package/lib/specs/agents/agents.d.ts +1 -0
- package/lib/specs/agents/agents.js +338 -284
- package/lib/specs/apps.d.ts +25 -0
- package/lib/specs/apps.js +1064 -0
- package/lib/specs/cogs.d.ts +19 -0
- package/lib/specs/cogs.js +736 -0
- package/lib/specs/events.d.ts +6 -1
- package/lib/specs/events.js +144 -1
- package/lib/specs/frames.d.ts +19 -0
- package/lib/specs/frames.js +444 -0
- package/lib/specs/gates.d.ts +22 -0
- package/lib/specs/gates.js +177 -0
- package/lib/specs/guards.d.ts +26 -0
- package/lib/specs/guards.js +930 -0
- package/lib/specs/index.d.ts +10 -0
- package/lib/specs/index.js +10 -0
- package/lib/specs/mcpServers.js +2 -2
- package/lib/specs/memory.js +4 -0
- package/lib/specs/modelProviders.d.ts +22 -0
- package/lib/specs/modelProviders.js +102 -0
- package/lib/specs/models.d.ts +31 -0
- package/lib/specs/models.js +215 -0
- package/lib/specs/ops.d.ts +15 -0
- package/lib/specs/ops.js +1272 -0
- package/lib/specs/outputs.js +2 -2
- package/lib/specs/skills.js +1 -1
- package/lib/specs/teams/teams.js +18 -18
- package/lib/specs/tracks.d.ts +15 -0
- package/lib/specs/tracks.js +87 -0
- package/lib/specs/uiPlugins.d.ts +16 -0
- package/lib/specs/uiPlugins.js +40 -0
- package/lib/types/agentspecs.d.ts +453 -2
- package/lib/types/memory.d.ts +2 -0
- package/lib/types/models.d.ts +52 -0
- package/package.json +2 -1
- package/scripts/check-cloudflare-models.py +122 -0
- package/scripts/codegen/agentspecs_clone.py +53 -0
- package/scripts/codegen/compose.py +65 -0
- package/scripts/codegen/generate_agents.py +14 -4
- package/scripts/codegen/generate_apps.py +454 -0
- package/scripts/codegen/generate_cogs.py +308 -0
- package/scripts/codegen/generate_frames.py +278 -0
- package/scripts/codegen/generate_memory.py +2 -0
- package/scripts/codegen/generate_model_providers.py +246 -0
- package/scripts/codegen/generate_models.py +135 -4
- package/scripts/codegen/generate_ops.py +388 -0
- package/scripts/codegen/generate_sandbox_agents.py +1 -1
- package/scripts/codegen/generate_ui_plugins.py +226 -0
- package/scripts/gen_workers.py +2 -2
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
# Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Generate Python and TypeScript code from YAML model-provider specifications.
|
|
7
|
+
|
|
8
|
+
A model provider is who serves a model (Anthropic, Amazon Bedrock, Cloudflare,
|
|
9
|
+
Ollama…): its site, its documentation, its terms of service, its privacy
|
|
10
|
+
policy, what it says about the data a request carries, and whether it runs
|
|
11
|
+
the model or the user's machine does. One YAML per provider under
|
|
12
|
+
``agentspecs/model-providers``; a model spec's ``provider`` names one.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
python generate_model_providers.py \\
|
|
16
|
+
--specs-dir agentspecs/agentspecs/model-providers \\
|
|
17
|
+
--python-output agent_runtimes/specs/model_providers.py \\
|
|
18
|
+
--typescript-output src/specs/modelProviders.ts
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import argparse
|
|
22
|
+
import sys
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
import yaml
|
|
27
|
+
from versioning import ensure_spec_version, version_suffix
|
|
28
|
+
|
|
29
|
+
FIELDS = ("website", "docs_url", "terms_url", "privacy_url", "data_usage_url")
|
|
30
|
+
CAMEL = {
|
|
31
|
+
"website": "website",
|
|
32
|
+
"docs_url": "docsUrl",
|
|
33
|
+
"terms_url": "termsUrl",
|
|
34
|
+
"privacy_url": "privacyUrl",
|
|
35
|
+
"data_usage_url": "dataUsageUrl",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def load_provider_specs(specs_dir: Path) -> list[dict[str, Any]]:
|
|
40
|
+
"""Load all model-provider YAML specifications from a directory."""
|
|
41
|
+
specs = []
|
|
42
|
+
for yaml_file in sorted(specs_dir.glob("*.yaml")):
|
|
43
|
+
with open(yaml_file) as f:
|
|
44
|
+
spec = yaml.safe_load(f)
|
|
45
|
+
ensure_spec_version(spec)
|
|
46
|
+
if spec["id"] != yaml_file.stem:
|
|
47
|
+
raise ValueError(
|
|
48
|
+
f"{yaml_file.name}: the file is named for id {yaml_file.stem!r}, the spec says {spec['id']!r}"
|
|
49
|
+
)
|
|
50
|
+
specs.append(spec)
|
|
51
|
+
return specs
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _const_name(provider_id: str) -> str:
|
|
55
|
+
return provider_id.replace("-", "_").replace(".", "_").upper()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _py(value: str) -> str:
|
|
59
|
+
return '"' + value.replace("\\", "\\\\").replace('"', '\\"') + '"'
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _ts(value: str) -> str:
|
|
63
|
+
return "'" + value.replace("\\", "\\\\").replace("'", "\\'") + "'"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
67
|
+
"""Generate Python code from model-provider specifications."""
|
|
68
|
+
lines = [
|
|
69
|
+
"# Copyright (c) 2025-2026 Datalayer, Inc.",
|
|
70
|
+
"# Distributed under the terms of the Modified BSD License.",
|
|
71
|
+
'"""',
|
|
72
|
+
"Model Provider Catalog.",
|
|
73
|
+
"",
|
|
74
|
+
"Who serves a model: the vendor's own API, a cloud that hosts it, or the",
|
|
75
|
+
"user's machine — with its site, documentation, terms, privacy policy and",
|
|
76
|
+
"what it says about the data a request carries.",
|
|
77
|
+
"",
|
|
78
|
+
"This file is AUTO-GENERATED from YAML specifications.",
|
|
79
|
+
"DO NOT EDIT MANUALLY - run 'make specs' to regenerate.",
|
|
80
|
+
'"""',
|
|
81
|
+
"",
|
|
82
|
+
"from typing import Dict",
|
|
83
|
+
"",
|
|
84
|
+
"from agent_runtimes.types import ModelProvider",
|
|
85
|
+
"",
|
|
86
|
+
"",
|
|
87
|
+
"# " + "=" * 76,
|
|
88
|
+
"# Model Provider Definitions",
|
|
89
|
+
"# " + "=" * 76,
|
|
90
|
+
"",
|
|
91
|
+
]
|
|
92
|
+
for spec in specs:
|
|
93
|
+
const = _const_name(spec["id"]) + version_suffix(spec["version"])
|
|
94
|
+
lines.extend(
|
|
95
|
+
[
|
|
96
|
+
f"{const} = ModelProvider(",
|
|
97
|
+
f" id={_py(spec['id'])},",
|
|
98
|
+
f" version={_py(str(spec['version']))},",
|
|
99
|
+
f" name={_py(spec['name'])},",
|
|
100
|
+
f" description={_py(str(spec.get('description', '')).strip())},",
|
|
101
|
+
]
|
|
102
|
+
)
|
|
103
|
+
for field in FIELDS:
|
|
104
|
+
value = spec.get(field)
|
|
105
|
+
if value:
|
|
106
|
+
lines.append(f" {field}={_py(str(value))},")
|
|
107
|
+
lines.append(f" hosting={_py(spec.get('hosting', 'cloud'))},")
|
|
108
|
+
lines.extend([")", ""])
|
|
109
|
+
lines.extend(
|
|
110
|
+
[
|
|
111
|
+
"",
|
|
112
|
+
"# " + "=" * 76,
|
|
113
|
+
"# Model Provider Catalog",
|
|
114
|
+
"# " + "=" * 76,
|
|
115
|
+
"",
|
|
116
|
+
"MODEL_PROVIDER_CATALOGUE: Dict[str, ModelProvider] = {",
|
|
117
|
+
]
|
|
118
|
+
)
|
|
119
|
+
for spec in specs:
|
|
120
|
+
lines.append(
|
|
121
|
+
f" {_py(spec['id'])}: {_const_name(spec['id']) + version_suffix(spec['version'])},"
|
|
122
|
+
)
|
|
123
|
+
lines.extend(
|
|
124
|
+
[
|
|
125
|
+
"}",
|
|
126
|
+
"",
|
|
127
|
+
"",
|
|
128
|
+
"def get_model_provider(provider_id: str) -> ModelProvider | None:",
|
|
129
|
+
' """The provider a model spec\'s `provider` names, or None."""',
|
|
130
|
+
" return MODEL_PROVIDER_CATALOGUE.get(provider_id)",
|
|
131
|
+
"",
|
|
132
|
+
"",
|
|
133
|
+
"def list_model_providers() -> list[ModelProvider]:",
|
|
134
|
+
" return list(MODEL_PROVIDER_CATALOGUE.values())",
|
|
135
|
+
"",
|
|
136
|
+
]
|
|
137
|
+
)
|
|
138
|
+
return "\n".join(lines)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
142
|
+
"""Generate TypeScript code from model-provider specifications."""
|
|
143
|
+
lines = [
|
|
144
|
+
"/*",
|
|
145
|
+
" * Copyright (c) 2025-2026 Datalayer, Inc.",
|
|
146
|
+
" * Distributed under the terms of the Modified BSD License.",
|
|
147
|
+
" */",
|
|
148
|
+
"",
|
|
149
|
+
"/**",
|
|
150
|
+
" * Model Provider Catalog.",
|
|
151
|
+
" *",
|
|
152
|
+
" * Who serves a model: the vendor's own API, a cloud that hosts it, or the",
|
|
153
|
+
" * user's machine — with its site, documentation, terms, privacy policy and",
|
|
154
|
+
" * what it says about the data a request carries.",
|
|
155
|
+
" *",
|
|
156
|
+
" * This file is AUTO-GENERATED from YAML specifications.",
|
|
157
|
+
" * DO NOT EDIT MANUALLY - run 'make specs' to regenerate.",
|
|
158
|
+
" */",
|
|
159
|
+
"",
|
|
160
|
+
"import type { ModelProvider } from '../types/models';",
|
|
161
|
+
"",
|
|
162
|
+
]
|
|
163
|
+
for spec in specs:
|
|
164
|
+
const = _const_name(spec["id"]) + version_suffix(spec["version"])
|
|
165
|
+
lines.extend(
|
|
166
|
+
[
|
|
167
|
+
f"export const {const}: ModelProvider = {{",
|
|
168
|
+
f" id: {_ts(spec['id'])},",
|
|
169
|
+
f" version: {_ts(str(spec['version']))},",
|
|
170
|
+
f" name: {_ts(spec['name'])},",
|
|
171
|
+
f" description: {_ts(str(spec.get('description', '')).strip())},",
|
|
172
|
+
]
|
|
173
|
+
)
|
|
174
|
+
for field in FIELDS:
|
|
175
|
+
value = spec.get(field)
|
|
176
|
+
if value:
|
|
177
|
+
lines.append(f" {CAMEL[field]}: {_ts(str(value))},")
|
|
178
|
+
lines.append(f" hosting: {_ts(spec.get('hosting', 'cloud'))},")
|
|
179
|
+
lines.extend(["};", ""])
|
|
180
|
+
lines.append(
|
|
181
|
+
"export const MODEL_PROVIDER_CATALOGUE: Record<string, ModelProvider> = {"
|
|
182
|
+
)
|
|
183
|
+
for spec in specs:
|
|
184
|
+
lines.append(
|
|
185
|
+
f" {_ts(spec['id'])}: {_const_name(spec['id']) + version_suffix(spec['version'])},"
|
|
186
|
+
)
|
|
187
|
+
lines.extend(
|
|
188
|
+
[
|
|
189
|
+
"};",
|
|
190
|
+
"",
|
|
191
|
+
"/** The provider a model spec's `provider` names, or undefined. */",
|
|
192
|
+
"export function getModelProvider(providerId: string): ModelProvider | undefined {",
|
|
193
|
+
" return MODEL_PROVIDER_CATALOGUE[providerId];",
|
|
194
|
+
"}",
|
|
195
|
+
"",
|
|
196
|
+
"export function listModelProviders(): ModelProvider[] {",
|
|
197
|
+
" return Object.values(MODEL_PROVIDER_CATALOGUE);",
|
|
198
|
+
"}",
|
|
199
|
+
"",
|
|
200
|
+
]
|
|
201
|
+
)
|
|
202
|
+
return "\n".join(lines)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def main():
|
|
206
|
+
parser = argparse.ArgumentParser(
|
|
207
|
+
description="Generate Python and TypeScript code from YAML model-provider specifications"
|
|
208
|
+
)
|
|
209
|
+
parser.add_argument(
|
|
210
|
+
"--specs-dir",
|
|
211
|
+
type=Path,
|
|
212
|
+
required=True,
|
|
213
|
+
help="Directory containing model-provider YAML files",
|
|
214
|
+
)
|
|
215
|
+
parser.add_argument(
|
|
216
|
+
"--python-output", type=Path, required=True, help="Output path for Python file"
|
|
217
|
+
)
|
|
218
|
+
parser.add_argument(
|
|
219
|
+
"--typescript-output",
|
|
220
|
+
type=Path,
|
|
221
|
+
required=True,
|
|
222
|
+
help="Output path for TypeScript file",
|
|
223
|
+
)
|
|
224
|
+
args = parser.parse_args()
|
|
225
|
+
|
|
226
|
+
if not args.specs_dir.exists():
|
|
227
|
+
print(f"Error: Specs directory not found: {args.specs_dir}", file=sys.stderr)
|
|
228
|
+
sys.exit(1)
|
|
229
|
+
specs = load_provider_specs(args.specs_dir)
|
|
230
|
+
if not specs:
|
|
231
|
+
print(
|
|
232
|
+
f"Warning: No model-provider specifications found in {args.specs_dir}",
|
|
233
|
+
file=sys.stderr,
|
|
234
|
+
)
|
|
235
|
+
return
|
|
236
|
+
args.python_output.parent.mkdir(parents=True, exist_ok=True)
|
|
237
|
+
args.python_output.write_text(generate_python_code(specs))
|
|
238
|
+
print(f"Generated Python code: {args.python_output}")
|
|
239
|
+
args.typescript_output.parent.mkdir(parents=True, exist_ok=True)
|
|
240
|
+
args.typescript_output.write_text(generate_typescript_code(specs))
|
|
241
|
+
print(f"Generated TypeScript code: {args.typescript_output}")
|
|
242
|
+
print(f"\n✓ Successfully generated code from {len(specs)} model-provider specs")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
if __name__ == "__main__":
|
|
246
|
+
main()
|
|
@@ -38,7 +38,14 @@ def _make_const_name(model_id: str) -> str:
|
|
|
38
38
|
|
|
39
39
|
E.g. 'anthropic:claude-sonnet-4-5-20250514' -> 'ANTHROPIC_CLAUDE_SONNET_4_5_20250514'
|
|
40
40
|
"""
|
|
41
|
-
|
|
41
|
+
# A Cloudflare id carries a vendor segment: "cloudflare:openai/gpt-oss-120b".
|
|
42
|
+
return (
|
|
43
|
+
model_id.upper()
|
|
44
|
+
.replace(":", "_")
|
|
45
|
+
.replace("-", "_")
|
|
46
|
+
.replace(".", "_")
|
|
47
|
+
.replace("/", "_")
|
|
48
|
+
)
|
|
42
49
|
|
|
43
50
|
|
|
44
51
|
def _pick_default(specs: list[dict]) -> Optional[dict]:
|
|
@@ -89,7 +96,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
89
96
|
"from enum import Enum",
|
|
90
97
|
"from typing import Dict, List, Optional",
|
|
91
98
|
"",
|
|
92
|
-
"from agent_runtimes.types import AIModel",
|
|
99
|
+
"from agent_runtimes.types import AIModel, ModelPricing",
|
|
93
100
|
"",
|
|
94
101
|
"",
|
|
95
102
|
"# " + "=" * 76,
|
|
@@ -102,10 +109,18 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
102
109
|
"",
|
|
103
110
|
]
|
|
104
111
|
|
|
105
|
-
# Generate enum members
|
|
112
|
+
# Generate enum members; an older id keeps its member, with its older value.
|
|
113
|
+
seen = set()
|
|
106
114
|
for spec in specs:
|
|
107
115
|
enum_name = _make_enum_name(spec["id"])
|
|
108
116
|
lines.append(f' {enum_name} = "{spec["id"]}"')
|
|
117
|
+
seen.add(enum_name)
|
|
118
|
+
for spec in specs:
|
|
119
|
+
for alias in spec.get("aliases") or []:
|
|
120
|
+
alias_name = _make_enum_name(alias)
|
|
121
|
+
if alias_name not in seen:
|
|
122
|
+
lines.append(f' {alias_name} = "{alias}" # before the id moved')
|
|
123
|
+
seen.add(alias_name)
|
|
109
124
|
|
|
110
125
|
lines.extend(
|
|
111
126
|
[
|
|
@@ -141,6 +156,11 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
141
156
|
f' name="{spec["name"]}",',
|
|
142
157
|
f' description="{spec.get("description", "")}",',
|
|
143
158
|
f' provider="{spec["provider"]}",',
|
|
159
|
+
*(
|
|
160
|
+
[f' provider_url="{spec["provider_url"]}",']
|
|
161
|
+
if spec.get("provider_url")
|
|
162
|
+
else []
|
|
163
|
+
),
|
|
144
164
|
f" default={spec.get('default', False)},",
|
|
145
165
|
# What is worth offering today, as distinct from what the platform
|
|
146
166
|
# knows how to talk to.
|
|
@@ -160,6 +180,30 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
160
180
|
if capabilities:
|
|
161
181
|
formatted = "[" + ", ".join(f'"{c}"' for c in capabilities) + "]"
|
|
162
182
|
model_lines.append(f" capabilities={formatted},")
|
|
183
|
+
if spec.get("billing"):
|
|
184
|
+
model_lines.append(f' billing="{spec["billing"]}",')
|
|
185
|
+
if spec.get("route"):
|
|
186
|
+
model_lines.append(f' route="{spec["route"]}",')
|
|
187
|
+
if spec.get("context_window") is not None:
|
|
188
|
+
model_lines.append(f" context_window={int(spec['context_window'])},")
|
|
189
|
+
if spec.get("zero_data_retention") is not None:
|
|
190
|
+
model_lines.append(
|
|
191
|
+
f" zero_data_retention={bool(spec['zero_data_retention'])},"
|
|
192
|
+
)
|
|
193
|
+
if spec.get("request_logging"):
|
|
194
|
+
model_lines.append(f' request_logging="{spec["request_logging"]}",')
|
|
195
|
+
aliases = spec.get("aliases") or []
|
|
196
|
+
if aliases:
|
|
197
|
+
model_lines.append(
|
|
198
|
+
" aliases=[" + ", ".join(f'"{a}"' for a in aliases) + "],"
|
|
199
|
+
)
|
|
200
|
+
pricing = spec.get("pricing") or {}
|
|
201
|
+
if pricing:
|
|
202
|
+
model_lines.append(
|
|
203
|
+
" pricing=ModelPricing("
|
|
204
|
+
f"input_usd_per_million={float(pricing['input_usd_per_million'])}, "
|
|
205
|
+
f"output_usd_per_million={float(pricing['output_usd_per_million'])}),"
|
|
206
|
+
)
|
|
163
207
|
|
|
164
208
|
model_lines.extend([")", ""])
|
|
165
209
|
lines.extend(model_lines)
|
|
@@ -180,6 +224,18 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
180
224
|
const_name = _make_const_name(model_id) + version_suffix(spec["version"])
|
|
181
225
|
lines.append(f' "{model_id}": {const_name},')
|
|
182
226
|
|
|
227
|
+
lines.extend(
|
|
228
|
+
[
|
|
229
|
+
"}",
|
|
230
|
+
"",
|
|
231
|
+
"# The ids a model answered to before its id moved, to the id it has now.",
|
|
232
|
+
"# Kept apart from the catalogue, which lists each model once.",
|
|
233
|
+
"AI_MODEL_ALIASES: Dict[str, str] = {",
|
|
234
|
+
]
|
|
235
|
+
)
|
|
236
|
+
for spec in specs:
|
|
237
|
+
for alias in spec.get("aliases") or []:
|
|
238
|
+
lines.append(f' "{alias}": "{spec["id"]}",')
|
|
183
239
|
lines.extend(
|
|
184
240
|
[
|
|
185
241
|
"}",
|
|
@@ -237,7 +293,7 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
237
293
|
" Returns:",
|
|
238
294
|
" The AIModel specification, or None if not found.",
|
|
239
295
|
' """',
|
|
240
|
-
" model = AI_MODEL_CATALOGUE.get(model_id)",
|
|
296
|
+
" model = AI_MODEL_CATALOGUE.get(AI_MODEL_ALIASES.get(model_id, model_id))",
|
|
241
297
|
" if model is not None:",
|
|
242
298
|
" return model",
|
|
243
299
|
" base, _, ver = model_id.rpartition(':')",
|
|
@@ -269,6 +325,19 @@ def generate_python_code(specs: list[dict[str, Any]]) -> str:
|
|
|
269
325
|
' """',
|
|
270
326
|
" return list(AI_MODEL_CATALOGUE.values())",
|
|
271
327
|
"",
|
|
328
|
+
"",
|
|
329
|
+
"def is_chat_model(model: AIModel) -> bool:",
|
|
330
|
+
' """A model a chat can run on: not a typed-judgment model (Jev), which',
|
|
331
|
+
" answers typed questions about a state and nothing else. A spec that",
|
|
332
|
+
" states no capability is read as a chat model.",
|
|
333
|
+
' """',
|
|
334
|
+
' return "judgments" not in model.capabilities',
|
|
335
|
+
"",
|
|
336
|
+
"",
|
|
337
|
+
"def list_chat_models() -> list[AIModel]:",
|
|
338
|
+
' """The models a person choosing a chat model is offered from."""',
|
|
339
|
+
" return [model for model in AI_MODEL_CATALOGUE.values() if is_chat_model(model)]",
|
|
340
|
+
"",
|
|
272
341
|
]
|
|
273
342
|
)
|
|
274
343
|
|
|
@@ -342,6 +411,11 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
342
411
|
f" name: '{spec['name']}',",
|
|
343
412
|
f" description: '{description}',",
|
|
344
413
|
f" provider: '{spec['provider']}',",
|
|
414
|
+
*(
|
|
415
|
+
[f" providerUrl: '{spec['provider_url']}',"]
|
|
416
|
+
if spec.get("provider_url")
|
|
417
|
+
else []
|
|
418
|
+
),
|
|
345
419
|
f" default: {str(spec.get('default', False)).lower()},",
|
|
346
420
|
f" available: {str(spec.get('available', False)).lower()},",
|
|
347
421
|
f" requiredEnvVars: {env_vars_formatted},",
|
|
@@ -358,6 +432,30 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
358
432
|
if capabilities:
|
|
359
433
|
formatted = "[" + ", ".join(f"'{c}'" for c in capabilities) + "]"
|
|
360
434
|
model_lines.append(f" capabilities: {formatted},")
|
|
435
|
+
if spec.get("billing"):
|
|
436
|
+
model_lines.append(f" billing: '{spec['billing']}',")
|
|
437
|
+
if spec.get("route"):
|
|
438
|
+
model_lines.append(f" route: '{spec['route']}',")
|
|
439
|
+
if spec.get("context_window") is not None:
|
|
440
|
+
model_lines.append(f" contextWindow: {int(spec['context_window'])},")
|
|
441
|
+
if spec.get("zero_data_retention") is not None:
|
|
442
|
+
model_lines.append(
|
|
443
|
+
f" zeroDataRetention: {'true' if spec['zero_data_retention'] else 'false'},"
|
|
444
|
+
)
|
|
445
|
+
if spec.get("request_logging"):
|
|
446
|
+
model_lines.append(f" requestLogging: '{spec['request_logging']}',")
|
|
447
|
+
aliases = spec.get("aliases") or []
|
|
448
|
+
if aliases:
|
|
449
|
+
model_lines.append(
|
|
450
|
+
" aliases: [" + ", ".join(f"'{a}'" for a in aliases) + "],"
|
|
451
|
+
)
|
|
452
|
+
pricing = spec.get("pricing") or {}
|
|
453
|
+
if pricing:
|
|
454
|
+
model_lines.append(
|
|
455
|
+
" pricing: { "
|
|
456
|
+
f"inputUsdPerMillion: {float(pricing['input_usd_per_million'])}, "
|
|
457
|
+
f"outputUsdPerMillion: {float(pricing['output_usd_per_million'])} }},"
|
|
458
|
+
)
|
|
361
459
|
model_lines.extend(
|
|
362
460
|
[
|
|
363
461
|
"};",
|
|
@@ -386,6 +484,39 @@ def generate_typescript_code(specs: list[dict[str, Any]]) -> str:
|
|
|
386
484
|
[
|
|
387
485
|
"};",
|
|
388
486
|
"",
|
|
487
|
+
"/**",
|
|
488
|
+
" * The ids a model answered to before its id moved, to the id it has now.",
|
|
489
|
+
" * Kept apart from the catalogue, which lists each model once.",
|
|
490
|
+
" */",
|
|
491
|
+
"export const AI_MODEL_ALIASES: Record<string, string> = {",
|
|
492
|
+
]
|
|
493
|
+
)
|
|
494
|
+
for spec in specs:
|
|
495
|
+
for alias in spec.get("aliases") or []:
|
|
496
|
+
lines.append(f" '{alias}': '{spec['id']}',")
|
|
497
|
+
lines.extend(
|
|
498
|
+
[
|
|
499
|
+
"};",
|
|
500
|
+
"",
|
|
501
|
+
"/** A model by its id, or by an id it had before; undefined when the catalogue has neither. */",
|
|
502
|
+
"export function getModel(modelId: string): AIModel | undefined {",
|
|
503
|
+
" return AI_MODEL_CATALOGUE[AI_MODEL_ALIASES[modelId] ?? modelId];",
|
|
504
|
+
"}",
|
|
505
|
+
"",
|
|
506
|
+
"/**",
|
|
507
|
+
" * A model a chat can run on: not a typed-judgment model (Jev), which",
|
|
508
|
+
" * answers typed questions about a state and nothing else. A spec that",
|
|
509
|
+
" * states no capability is read as a chat model.",
|
|
510
|
+
" */",
|
|
511
|
+
"export function isChatModel(model: AIModel): boolean {",
|
|
512
|
+
" return !(model.capabilities ?? []).includes('judgments');",
|
|
513
|
+
"}",
|
|
514
|
+
"",
|
|
515
|
+
"/** The models a person choosing a chat model is offered from. */",
|
|
516
|
+
"export function listChatModels(): AIModel[] {",
|
|
517
|
+
" return Object.values(AI_MODEL_CATALOGUE).filter(isChatModel);",
|
|
518
|
+
"}",
|
|
519
|
+
"",
|
|
389
520
|
]
|
|
390
521
|
)
|
|
391
522
|
|