cortico 0.1.0

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.
Files changed (311) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +47 -0
  3. package/package.json +43 -0
  4. package/src/boot.ts +46 -0
  5. package/src/bot.ts +1342 -0
  6. package/src/config-file.ts +41 -0
  7. package/src/core/README.md +92 -0
  8. package/src/core/billing.ts +26 -0
  9. package/src/core/blobs.ts +106 -0
  10. package/src/core/bus.ts +311 -0
  11. package/src/core/config-schema.ts +201 -0
  12. package/src/core/config.ts +262 -0
  13. package/src/core/core.ts +699 -0
  14. package/src/core/cost.ts +244 -0
  15. package/src/core/event-store.ts +304 -0
  16. package/src/core/fork.ts +113 -0
  17. package/src/core/generation.ts +118 -0
  18. package/src/core/instance-lock.ts +215 -0
  19. package/src/core/ipc-logger.ts +105 -0
  20. package/src/core/language.ts +62 -0
  21. package/src/core/log-context.ts +38 -0
  22. package/src/core/loop.ts +1858 -0
  23. package/src/core/markers.ts +48 -0
  24. package/src/core/prefix.ts +121 -0
  25. package/src/core/run.ts +95 -0
  26. package/src/core/secrets.ts +18 -0
  27. package/src/core/session.ts +56 -0
  28. package/src/core/sessions.ts +261 -0
  29. package/src/core/state.ts +104 -0
  30. package/src/core/template.ts +72 -0
  31. package/src/core/timers.ts +129 -0
  32. package/src/core/tool-log.ts +135 -0
  33. package/src/core/transcript.ts +92 -0
  34. package/src/core/truncate.ts +121 -0
  35. package/src/core/types.ts +1140 -0
  36. package/src/core/usage-log.ts +79 -0
  37. package/src/core/util.ts +386 -0
  38. package/src/deploy-listing.ts +160 -0
  39. package/src/deploy.ts +162 -0
  40. package/src/extensions/README.md +72 -0
  41. package/src/extensions/dry-mount.ts +390 -0
  42. package/src/extensions/manifest.ts +153 -0
  43. package/src/extensions/runtime.ts +42 -0
  44. package/src/extensions.ts +519 -0
  45. package/src/launcher.ts +285 -0
  46. package/src/paths.ts +155 -0
  47. package/src/protocol/open-responses/LICENSE +201 -0
  48. package/src/protocol/open-responses/README.md +14 -0
  49. package/src/protocol/open-responses/context-helpers.ts +27 -0
  50. package/src/protocol/open-responses/context-log.ts +59 -0
  51. package/src/protocol/open-responses/context.ts +89 -0
  52. package/src/protocol/open-responses/generated.ts +115 -0
  53. package/src/protocol/open-responses/index.ts +33 -0
  54. package/src/protocol/open-responses/openapi.json +4230 -0
  55. package/src/protocol/open-responses/stream.ts +146 -0
  56. package/src/protocol/open-responses/tokens.ts +31 -0
  57. package/src/providers/README.md +86 -0
  58. package/src/providers/base.ts +111 -0
  59. package/src/providers/configuration.ts +97 -0
  60. package/src/providers/console/config.ts +21 -0
  61. package/src/providers/console/settings.ts +443 -0
  62. package/src/providers/console/strings.ts +74 -0
  63. package/src/providers/console/types.ts +11 -0
  64. package/src/providers/llamacpp/archive.ts +148 -0
  65. package/src/providers/llamacpp/catalog.ts +247 -0
  66. package/src/providers/llamacpp/config.ts +36 -0
  67. package/src/providers/llamacpp/console/client.ts +6 -0
  68. package/src/providers/llamacpp/console/models-panel.ts +120 -0
  69. package/src/providers/llamacpp/console/runtime-panel.ts +173 -0
  70. package/src/providers/llamacpp/console/server.ts +153 -0
  71. package/src/providers/llamacpp/index.ts +95 -0
  72. package/src/providers/llamacpp/native.ts +51 -0
  73. package/src/providers/llamacpp/options.ts +117 -0
  74. package/src/providers/llamacpp/runtime-store.ts +139 -0
  75. package/src/providers/llamacpp/runtime.ts +214 -0
  76. package/src/providers/llamacpp/server.ts +286 -0
  77. package/src/providers/llamacpp/strings.ts +230 -0
  78. package/src/providers/openai-responses-compat/config.ts +20 -0
  79. package/src/providers/openai-responses-compat/index.ts +89 -0
  80. package/src/providers/openai-responses-compat/native.ts +127 -0
  81. package/src/providers/openai-responses-compat/strings.ts +19 -0
  82. package/src/providers/pricebook.ts +75 -0
  83. package/src/providers/registry.ts +137 -0
  84. package/src/providers/strings.ts +71 -0
  85. package/src/providers/transport/chat.ts +156 -0
  86. package/src/providers/transport/errors.ts +48 -0
  87. package/src/providers/transport/history.ts +68 -0
  88. package/src/providers/transport/native-input.ts +83 -0
  89. package/src/providers/transport/native-types.ts +13 -0
  90. package/src/providers/transport/response-assembly.ts +191 -0
  91. package/src/providers/transport/response-http.ts +196 -0
  92. package/src/providers/transport/response-meters.ts +27 -0
  93. package/src/providers/transport/responses-input.ts +56 -0
  94. package/src/web/README.md +84 -0
  95. package/src/web/client/console-pages/builtins/llm-settings/panel.ts +625 -0
  96. package/src/web/client/console-pages/builtins/llm-settings/pricing-panel.ts +168 -0
  97. package/src/web/client/console-pages/builtins/llm-settings/strings.ts +213 -0
  98. package/src/web/client/console-pages/builtins.ts +10 -0
  99. package/src/web/client/console-pages/context.ts +135 -0
  100. package/src/web/client/console-pages/host.ts +633 -0
  101. package/src/web/client/console-pages/loader.ts +132 -0
  102. package/src/web/client/console-pages/strings.ts +90 -0
  103. package/src/web/client/console-pages/tools/strings.ts +49 -0
  104. package/src/web/client/console-pages/tools/view.ts +215 -0
  105. package/src/web/client/core/api.ts +200 -0
  106. package/src/web/client/core/language.ts +56 -0
  107. package/src/web/client/core/lifecycle.ts +132 -0
  108. package/src/web/client/core/router.ts +199 -0
  109. package/src/web/client/core/stream.ts +157 -0
  110. package/src/web/client/core/websocket.ts +82 -0
  111. package/src/web/client/features/appearance/index.ts +333 -0
  112. package/src/web/client/features/appearance/strings.ts +87 -0
  113. package/src/web/client/features/config/index.ts +11 -0
  114. package/src/web/client/features/config/strings.ts +41 -0
  115. package/src/web/client/features/config/view.ts +428 -0
  116. package/src/web/client/features/core/events.ts +197 -0
  117. package/src/web/client/features/core/index.ts +254 -0
  118. package/src/web/client/features/core/run.ts +56 -0
  119. package/src/web/client/features/core/runlog.ts +214 -0
  120. package/src/web/client/features/core/sessions.ts +53 -0
  121. package/src/web/client/features/core/strings.ts +157 -0
  122. package/src/web/client/features/extensions/index.ts +422 -0
  123. package/src/web/client/features/extensions/strings.ts +164 -0
  124. package/src/web/client/features/feature.ts +93 -0
  125. package/src/web/client/features/live/context.ts +280 -0
  126. package/src/web/client/features/live/fork.ts +156 -0
  127. package/src/web/client/features/live/index.ts +392 -0
  128. package/src/web/client/features/live/onboarding.ts +113 -0
  129. package/src/web/client/features/live/protocol.ts +122 -0
  130. package/src/web/client/features/live/sessions.ts +71 -0
  131. package/src/web/client/features/live/status.ts +68 -0
  132. package/src/web/client/features/live/strings.ts +252 -0
  133. package/src/web/client/features/live/timeline.ts +590 -0
  134. package/src/web/client/features/prompts/editor.ts +292 -0
  135. package/src/web/client/features/prompts/index.ts +346 -0
  136. package/src/web/client/features/prompts/strings.ts +136 -0
  137. package/src/web/client/features/prompts/view.ts +256 -0
  138. package/src/web/client/features/providers/index.ts +100 -0
  139. package/src/web/client/features/providers/strings.ts +23 -0
  140. package/src/web/client/features/settings/general.ts +28 -0
  141. package/src/web/client/features/settings/index.ts +82 -0
  142. package/src/web/client/features/settings/strings.ts +33 -0
  143. package/src/web/client/features/storage/index.ts +1 -0
  144. package/src/web/client/features/storage/strings.ts +45 -0
  145. package/src/web/client/features/storage/view.ts +154 -0
  146. package/src/web/client/features/usage/chart.ts +486 -0
  147. package/src/web/client/features/usage/index.ts +407 -0
  148. package/src/web/client/features/usage/labels.ts +46 -0
  149. package/src/web/client/features/usage/range.ts +36 -0
  150. package/src/web/client/features/usage/state.ts +37 -0
  151. package/src/web/client/features/usage/strings.ts +191 -0
  152. package/src/web/client/features/usage/tooltip.ts +86 -0
  153. package/src/web/client/features/usage/types.ts +96 -0
  154. package/src/web/client/features/worlds/index.ts +424 -0
  155. package/src/web/client/features/worlds/strings.ts +121 -0
  156. package/src/web/client/main.ts +273 -0
  157. package/src/web/client/shell/avatar.ts +233 -0
  158. package/src/web/client/shell/index.ts +549 -0
  159. package/src/web/client/shell/strings.ts +89 -0
  160. package/src/web/client/theme/handoff.ts +43 -0
  161. package/src/web/client/theme/palette.ts +109 -0
  162. package/src/web/client/theme/registry.ts +130 -0
  163. package/src/web/client/theme/storage.ts +72 -0
  164. package/src/web/client/theme/strings.ts +123 -0
  165. package/src/web/client/theme/studio.ts +366 -0
  166. package/src/web/client/ui/actions.ts +109 -0
  167. package/src/web/client/ui/data.ts +185 -0
  168. package/src/web/client/ui/dom.ts +23 -0
  169. package/src/web/client/ui/fields.ts +191 -0
  170. package/src/web/client/ui/format.ts +75 -0
  171. package/src/web/client/ui/icons.ts +183 -0
  172. package/src/web/client/ui/images.ts +91 -0
  173. package/src/web/client/ui/index.ts +89 -0
  174. package/src/web/client/ui/lamp.ts +125 -0
  175. package/src/web/client/ui/log.ts +98 -0
  176. package/src/web/client/ui/overlay.ts +184 -0
  177. package/src/web/client/ui/page.ts +8 -0
  178. package/src/web/client/ui/prompt-input.tsx +311 -0
  179. package/src/web/client/ui/sheet.ts +131 -0
  180. package/src/web/client/ui/strings.ts +71 -0
  181. package/src/web/console-pages.ts +367 -0
  182. package/src/web/files.ts +107 -0
  183. package/src/web/path-picker.ts +296 -0
  184. package/src/web/public/index.html +17 -0
  185. package/src/web/public/styles.css +1715 -0
  186. package/src/web/server.ts +1883 -0
  187. package/src/web/shared/client-panel.ts +603 -0
  188. package/src/web/shared/console-protocol.ts +560 -0
  189. package/src/web/shared/css.d.ts +1 -0
  190. package/src/web/shared/path-picker.ts +23 -0
  191. package/src/web/shared/theme.ts +256 -0
  192. package/src/web/theme-store.ts +37 -0
  193. package/src/world.ts +449 -0
  194. package/src/worlds/bilibili/ENV_PROMPT.md +5 -0
  195. package/src/worlds/bilibili/README.md +176 -0
  196. package/src/worlds/bilibili/audience-admission.ts +995 -0
  197. package/src/worlds/bilibili/client.ts +528 -0
  198. package/src/worlds/bilibili/coalescing-buffer.ts +125 -0
  199. package/src/worlds/bilibili/config.ts +185 -0
  200. package/src/worlds/bilibili/console/client.ts +202 -0
  201. package/src/worlds/bilibili/definition.ts +35 -0
  202. package/src/worlds/bilibili/gift-frame.ts +131 -0
  203. package/src/worlds/bilibili/normalize.ts +452 -0
  204. package/src/worlds/bilibili/overlay/announcement.ts +76 -0
  205. package/src/worlds/bilibili/overlay/assets.ts +82 -0
  206. package/src/worlds/bilibili/overlay/model.ts +494 -0
  207. package/src/worlds/bilibili/overlay/project.ts +202 -0
  208. package/src/worlds/bilibili/overlay/server.ts +286 -0
  209. package/src/worlds/bilibili/overlay/types.ts +217 -0
  210. package/src/worlds/bilibili/overlay/web/app.js +690 -0
  211. package/src/worlds/bilibili/overlay/web/editor.css +297 -0
  212. package/src/worlds/bilibili/overlay/web/editor.html +102 -0
  213. package/src/worlds/bilibili/overlay/web/editor.js +2116 -0
  214. package/src/worlds/bilibili/overlay/web/overlay.html +13 -0
  215. package/src/worlds/bilibili/overlay/web/styles.css +258 -0
  216. package/src/worlds/bilibili/protobuf.ts +123 -0
  217. package/src/worlds/bilibili/wire.ts +94 -0
  218. package/src/worlds/bilibili/world.ts +1389 -0
  219. package/src/worlds/console-fixture/ENV_PROMPT.md +1 -0
  220. package/src/worlds/console-fixture/console/client.ts +72 -0
  221. package/src/worlds/console-fixture/world.ts +73 -0
  222. package/src/worlds/index.ts +15 -0
  223. package/src/worlds/minecraft/ENV_PROMPT.md +32 -0
  224. package/src/worlds/minecraft/ENV_PROMPT_CAMERA.md +2 -0
  225. package/src/worlds/minecraft/LICENSE-mineflayer-pathfinder.txt +21 -0
  226. package/src/worlds/minecraft/README.md +917 -0
  227. package/src/worlds/minecraft/blueprint-plan.ts +915 -0
  228. package/src/worlds/minecraft/blueprint-registry.ts +566 -0
  229. package/src/worlds/minecraft/blueprint-repair.ts +236 -0
  230. package/src/worlds/minecraft/blueprint-resource.ts +272 -0
  231. package/src/worlds/minecraft/blueprint.ts +492 -0
  232. package/src/worlds/minecraft/body-lease.ts +340 -0
  233. package/src/worlds/minecraft/bridge.ts +749 -0
  234. package/src/worlds/minecraft/check.ts +651 -0
  235. package/src/worlds/minecraft/chests.ts +292 -0
  236. package/src/worlds/minecraft/client-launch.ts +272 -0
  237. package/src/worlds/minecraft/client-options.ts +85 -0
  238. package/src/worlds/minecraft/client-skins.ts +164 -0
  239. package/src/worlds/minecraft/client-window.ps1 +91 -0
  240. package/src/worlds/minecraft/client.ts +423 -0
  241. package/src/worlds/minecraft/combat-context.ts +109 -0
  242. package/src/worlds/minecraft/combat.ts +1552 -0
  243. package/src/worlds/minecraft/config.ts +499 -0
  244. package/src/worlds/minecraft/console/access.ts +218 -0
  245. package/src/worlds/minecraft/console/client.ts +244 -0
  246. package/src/worlds/minecraft/console/log.ts +110 -0
  247. package/src/worlds/minecraft/console/mount.ts +420 -0
  248. package/src/worlds/minecraft/console/skin.ts +257 -0
  249. package/src/worlds/minecraft/console/style.css +154 -0
  250. package/src/worlds/minecraft/console/world.ts +449 -0
  251. package/src/worlds/minecraft/deaths.ts +135 -0
  252. package/src/worlds/minecraft/definition.ts +18 -0
  253. package/src/worlds/minecraft/engine-child.ts +274 -0
  254. package/src/worlds/minecraft/engine-ipc.ts +98 -0
  255. package/src/worlds/minecraft/entity-facts.ts +132 -0
  256. package/src/worlds/minecraft/escape.ts +407 -0
  257. package/src/worlds/minecraft/executor.ts +14494 -0
  258. package/src/worlds/minecraft/explored.ts +156 -0
  259. package/src/worlds/minecraft/geometry.ts +205 -0
  260. package/src/worlds/minecraft/goal-plan.ts +647 -0
  261. package/src/worlds/minecraft/item-break.ts +126 -0
  262. package/src/worlds/minecraft/item-facts.ts +146 -0
  263. package/src/worlds/minecraft/item-pick.ts +67 -0
  264. package/src/worlds/minecraft/level-dat.ts +176 -0
  265. package/src/worlds/minecraft/log.ts +132 -0
  266. package/src/worlds/minecraft/mineflayer-fixes.ts +1180 -0
  267. package/src/worlds/minecraft/names.ts +377 -0
  268. package/src/worlds/minecraft/pathfinder-lib.d.ts +13 -0
  269. package/src/worlds/minecraft/pathfinder-perf.ts +796 -0
  270. package/src/worlds/minecraft/piglin.ts +49 -0
  271. package/src/worlds/minecraft/policy.ts +547 -0
  272. package/src/worlds/minecraft/precheck.ts +655 -0
  273. package/src/worlds/minecraft/proxy.ts +445 -0
  274. package/src/worlds/minecraft/ranged.ts +635 -0
  275. package/src/worlds/minecraft/readouts.ts +102 -0
  276. package/src/worlds/minecraft/round.ts +26 -0
  277. package/src/worlds/minecraft/search-observation.ts +92 -0
  278. package/src/worlds/minecraft/server-config.ts +467 -0
  279. package/src/worlds/minecraft/server.ts +658 -0
  280. package/src/worlds/minecraft/show.ts +84 -0
  281. package/src/worlds/minecraft/skills.ts +1763 -0
  282. package/src/worlds/minecraft/terrain.ts +1735 -0
  283. package/src/worlds/minecraft/window.ts +96 -0
  284. package/src/worlds/minecraft/works.ts +179 -0
  285. package/src/worlds/minecraft/world.ts +6438 -0
  286. package/src/worlds/qq/ENV_PROMPT.md +5 -0
  287. package/src/worlds/qq/config.ts +108 -0
  288. package/src/worlds/qq/console/client.ts +126 -0
  289. package/src/worlds/qq/console/events.ts +155 -0
  290. package/src/worlds/qq/console/gate.ts +232 -0
  291. package/src/worlds/qq/console/roster.ts +203 -0
  292. package/src/worlds/qq/console/style.css +65 -0
  293. package/src/worlds/qq/conversation.ts +28 -0
  294. package/src/worlds/qq/definition.ts +62 -0
  295. package/src/worlds/qq/driver.ts +375 -0
  296. package/src/worlds/qq/history-tools.ts +209 -0
  297. package/src/worlds/qq/normalize.ts +323 -0
  298. package/src/worlds/qq/qface-map.ts +282 -0
  299. package/src/worlds/qq/vision-prompt.ts +27 -0
  300. package/src/worlds/qq/vision.ts +642 -0
  301. package/src/worlds/qq/vlm.ts +142 -0
  302. package/src/worlds/qq/world.ts +1503 -0
  303. package/src/worlds/terminal/ENV_PROMPT.md +11 -0
  304. package/src/worlds/terminal/config.ts +9 -0
  305. package/src/worlds/terminal/definition.ts +14 -0
  306. package/src/worlds/terminal/world.ts +700 -0
  307. package/src/worlds/websearch/ENV_PROMPT.md +1 -0
  308. package/src/worlds/websearch/brave-client.ts +261 -0
  309. package/src/worlds/websearch/config.ts +75 -0
  310. package/src/worlds/websearch/definition.ts +20 -0
  311. package/src/worlds/websearch/world.ts +171 -0
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Named-endpoint operations for the runtime and model sections of the endpoint page.
3
+ * Runtime operations manage installation, launch configuration and the process; model operations
4
+ * call llama-server /models*.
5
+ */
6
+ import type { ConsolePageContribution } from '../../../web/shared/console-protocol.ts';
7
+ import type { ProviderConsoleHost } from '../../console/types.ts';
8
+ import type { RouterCatalog, RouterModel } from '../catalog.ts';
9
+ import { LAUNCH_DEFAULTS, PINNED_RELEASE, backendChoices, defaultBackend, llamacppOptions, type LaunchOptions } from '../options.ts';
10
+ import type { LlamaRuntime } from '../runtime.ts';
11
+ import type { RuntimeState } from '../runtime.ts';
12
+ import { runtimeConfig } from '../config.ts';
13
+ import { getByPath, type ConfigGroup, type ConfigValues } from '../../../core/config-schema.ts';
14
+ import { text } from '../strings.ts';
15
+
16
+ interface Control {
17
+ runtime: LlamaRuntime;
18
+ catalog: RouterCatalog;
19
+ }
20
+
21
+ export interface ModelsState {
22
+ name: string;
23
+ reachable: boolean;
24
+ cacheDir: string;
25
+ localModelsDir: string;
26
+ models: RouterModel[];
27
+ }
28
+
29
+ export type RuntimePanelState = RuntimeState & {
30
+ name: string;
31
+ config: Array<{ group: ConfigGroup; values: ConfigValues }>;
32
+ };
33
+
34
+ /** The launch fields the panel edits, each optional and applied over the stored values. */
35
+ type LaunchPatch = Partial<Record<keyof LaunchOptions, unknown>>;
36
+
37
+ export function llamacppConsole(host: ProviderConsoleHost): Partial<ConsolePageContribution> {
38
+ const S = text(host.language);
39
+ const control = (name: string): Control => host.instance(name).control as Control;
40
+ const body = (args: unknown[]): Record<string, unknown> => {
41
+ const [raw] = args;
42
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error(S.bodyRequired);
43
+ const value = raw as Record<string, unknown>;
44
+ if (typeof value.name !== 'string') throw new Error(S.instanceNameRequired);
45
+ return value;
46
+ };
47
+ const entryOf = (name: string) => {
48
+ const found = host.entries().find((entry) => entry.name === name);
49
+ if (!found) throw new Error(S.instanceNameRequired);
50
+ return found.entry;
51
+ };
52
+ return {
53
+ config: [],
54
+ panels: [
55
+ { id: 'runtime', title: S.runtimePanel, description: S.runtimePanelDescription, slot: 'instance' },
56
+ { id: 'models', title: S.modelsPanel, description: S.modelsPanelDescription, slot: 'instance' },
57
+ ],
58
+ invoke: async (panel, method, args) => {
59
+ if (panel === 'runtime') {
60
+ const value = body(args);
61
+ const name = value.name as string;
62
+ if (method === 'state') {
63
+ const entry = entryOf(name);
64
+ const prefix = `providers.${name}.`;
65
+ const config = runtimeConfig(name, entry, host.language).map((group) => ({
66
+ group,
67
+ values: Object.fromEntries(Object.keys(group.schema.properties).map((path) => [
68
+ path, getByPath(entry as unknown as Record<string, unknown>, path.slice(prefix.length)) ?? '',
69
+ ])) as ConfigValues,
70
+ }));
71
+ return { name, ...(await control(name).runtime.state(host.language)), config } satisfies RuntimePanelState;
72
+ }
73
+ if (method === 'enable') {
74
+ const entry = entryOf(name);
75
+ const options = llamacppOptions(entry);
76
+ const backend = typeof value.backend === 'string' && backendChoices().includes(value.backend) ? value.backend : defaultBackend();
77
+ host.save(name, {
78
+ ...entry,
79
+ options: {
80
+ ...entry.options,
81
+ runtime: { release: typeof value.release === 'string' && value.release.trim() ? value.release.trim() : PINNED_RELEASE, backend },
82
+ launch: { ...LAUNCH_DEFAULTS, ...(options.launch ?? {}) },
83
+ },
84
+ });
85
+ return { ok: true };
86
+ }
87
+ if (method === 'disable') {
88
+ await control(name).runtime.stop(host.language);
89
+ const entry = entryOf(name);
90
+ const { runtime: _runtime, launch: _launch, ...rest } = entry.options ?? {};
91
+ host.save(name, { ...entry, options: rest });
92
+ return { ok: true };
93
+ }
94
+ if (method === 'configure') {
95
+ const entry = entryOf(name);
96
+ const options = llamacppOptions(entry);
97
+ if (!options.runtime) throw new Error(S.notManaged);
98
+ const runtimeOptions = { ...options.runtime };
99
+ if (typeof value.release === 'string') runtimeOptions.release = value.release.trim();
100
+ if (typeof value.backend === 'string') runtimeOptions.backend = value.backend;
101
+ if (typeof value.runtimeDir === 'string') {
102
+ const dir = value.runtimeDir.trim();
103
+ if (dir) runtimeOptions.runtimeDir = dir;
104
+ else delete runtimeOptions.runtimeDir;
105
+ }
106
+ const launch = { ...LAUNCH_DEFAULTS, ...(options.launch ?? {}), ...((value.launch ?? {}) as LaunchPatch) };
107
+ host.save(name, {
108
+ ...entry,
109
+ options: {
110
+ ...entry.options,
111
+ runtime: runtimeOptions,
112
+ launch,
113
+ ...(value.autoStart === undefined ? {} : { autoStart: value.autoStart }),
114
+ },
115
+ });
116
+ return { ok: true };
117
+ }
118
+ if (method === 'install') {
119
+ await control(name).runtime.install(host.language);
120
+ return { ok: true };
121
+ }
122
+ if (method === 'start') return control(name).runtime.start(host.language);
123
+ if (method === 'stop') return control(name).runtime.stop(host.language);
124
+ throw new Error(S.unknownMethod);
125
+ }
126
+ if (panel === 'models') {
127
+ const value = body(args);
128
+ const name = value.name as string;
129
+ const { runtime, catalog } = control(name);
130
+ if (method === 'state') {
131
+ const dirs = runtime.modelDirs();
132
+ try {
133
+ return { name, reachable: true, ...dirs, models: await catalog.list() } satisfies ModelsState;
134
+ } catch {
135
+ return { name, reachable: false, ...dirs, models: [] } satisfies ModelsState;
136
+ }
137
+ }
138
+ if (method === 'reload') {
139
+ await catalog.list(true);
140
+ return { ok: true };
141
+ }
142
+ if (typeof value.model !== 'string' || !value.model.trim()) throw new Error(S.modelIdRequired);
143
+ const model = value.model.trim();
144
+ if (method === 'pull') await catalog.download(model);
145
+ else if (method === 'load') await catalog.load(model);
146
+ else if (method === 'unload' || method === 'cancel') await catalog.unload(model);
147
+ else throw new Error(S.unknownMethod);
148
+ return { ok: true };
149
+ }
150
+ throw new Error(S.unknownPanel);
151
+ },
152
+ };
153
+ }
@@ -0,0 +1,95 @@
1
+ import type { ProviderModule } from '../base.ts';
2
+ import type { ReasoningTier } from '../../core/types.ts';
3
+ import type { Language } from '../../core/language.ts';
4
+ import { isContextOverflow } from '../transport/errors.ts';
5
+ import { modelsRoot, runtimesRoot } from '../../paths.ts';
6
+ import { RouterCatalog } from './catalog.ts';
7
+ import { runtimeConfig } from './config.ts';
8
+ import { llamacppConsole } from './console/server.ts';
9
+ import { LlamaCppProvider } from './native.ts';
10
+ import { backendChoices, llamacppOptions, normalizeLlamaCpp } from './options.ts';
11
+ import { LlamaRuntime } from './runtime.ts';
12
+ import { nullLogger } from '../../core/util.ts';
13
+ import { text } from './strings.ts';
14
+
15
+ /** Two tiers: the transport sends the template's thinking switch, effort only when the operator typed one. */
16
+ const reasoningTiers = (language: Language): ReasoningTier[] => {
17
+ const S = text(language);
18
+ return [
19
+ { id: 'off', label: S.tierOff, thinking: false },
20
+ { id: 'on', label: S.tierOn, thinking: true },
21
+ ];
22
+ };
23
+
24
+ export default {
25
+ id: 'llamacpp',
26
+ title: 'llama.cpp',
27
+ defaultBaseUrl: 'http://127.0.0.1:8090/v1',
28
+ baseUrlSuggestions: ['http://127.0.0.1:8090/v1', 'http://127.0.0.1:8080/v1'],
29
+ normalize: normalizeLlamaCpp,
30
+ console: llamacppConsole,
31
+ config: runtimeConfig,
32
+ reasoningTiers: reasoningTiers('zh'),
33
+ localize: (language) => ({ reasoningTiers: reasoningTiers(language) }),
34
+ serviceTiers: [],
35
+ validateEntry: (entry, language) => {
36
+ const S = text(language);
37
+ const options = llamacppOptions(entry);
38
+ if (options.autoStart !== undefined && typeof options.autoStart !== 'boolean') throw new Error(S.autoStartBoolean);
39
+ if (!options.runtime) return;
40
+ const { runtime, launch } = options;
41
+ if (typeof runtime.release !== 'string' || !runtime.release.trim()) throw new Error(S.releaseRequired);
42
+ if (runtime.runtimeDir !== undefined && typeof runtime.runtimeDir !== 'string') throw new Error(S.runtimeDirString);
43
+ if (!runtime.runtimeDir && !backendChoices().includes(runtime.backend)) throw new Error(S.backendUnsupported(String(runtime.backend)));
44
+ if (launch) {
45
+ for (const key of ['contextSize', 'parallel'] as const)
46
+ if (!Number.isInteger(launch[key]) || launch[key] <= 0) throw new Error(S.positiveInteger(key));
47
+ if (!Number.isInteger(launch.nGpuLayers) || launch.nGpuLayers < 0) throw new Error(S.nonNegativeInteger('nGpuLayers'));
48
+ if (typeof launch.extraArgs !== 'string') throw new Error(S.extraArgsString);
49
+ }
50
+ },
51
+ contextOverflow: isContextOverflow,
52
+ availability: (name, entry, language) => {
53
+ if (!llamacppOptions(entry).runtime) return { ready: true };
54
+ const runtime = new LlamaRuntime({
55
+ name,
56
+ entry: () => entry,
57
+ secret: () => '',
58
+ log: nullLogger(),
59
+ roots: { runtimes: runtimesRoot(), models: modelsRoot() },
60
+ });
61
+ if (runtime.installState(language).phase === 'installed') return { ready: true };
62
+ return { ready: false, reason: text(language).runtimeNotInstalled };
63
+ },
64
+ create(name, entry, host) {
65
+ const current = host.currentEntry ?? (() => entry);
66
+ const apiKey = entry.secret ? host.secret(entry.secret) : undefined;
67
+ const catalog = new RouterCatalog(() => ({ baseUrl: current().baseUrl, apiKey }));
68
+ const createRuntime = () =>
69
+ new LlamaRuntime({
70
+ name,
71
+ entry: current,
72
+ secret: host.secret,
73
+ log: host.log,
74
+ roots: { runtimes: runtimesRoot(), models: modelsRoot() },
75
+ });
76
+ const runtime = host.resource?.('runtime', createRuntime) ?? createRuntime();
77
+ return {
78
+ control: { runtime, catalog },
79
+ listModels: () => catalog.listModels(),
80
+ contextWindow: (model) => catalog.contextWindow(model),
81
+ compatibilityKey: () => [name],
82
+ start: () => (llamacppOptions(current()).autoStart === true ? runtime.start() : Promise.resolve(undefined)),
83
+ stop: async () => {
84
+ catalog.stop();
85
+ await runtime.stop();
86
+ },
87
+ client: new LlamaCppProvider({
88
+ baseUrl: entry.baseUrl,
89
+ apiKey,
90
+ log: host.log,
91
+ media: { enabled: () => entry.multimodal === true, read: host.readBlob },
92
+ }),
93
+ };
94
+ },
95
+ } satisfies ProviderModule;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Chat Completions against llama-server. Thinking is the template's `enable_thinking` switch
3
+ * sent per request through `chat_template_kwargs`; templates without that variable ignore it.
4
+ * The server returns the chain of thought as `reasoning_content` (`--reasoning-format deepseek`),
5
+ * which the shared Chat assembly already maps; past thinking is not replayed.
6
+ */
7
+ import type { NativeChatMessage } from '../transport/native-types.ts';
8
+ import type { ModelSpec, ToolSchema, Logger } from '../../core/types.ts';
9
+ import { nullLogger } from '../../core/util.ts';
10
+ import { OpenAIHttpClient } from '../transport/chat.ts';
11
+ import { mapTools, renderMessagesWithMedia, type CompatMediaOptions } from '../transport/history.ts';
12
+
13
+ export function buildLlamaCppRequestBody(
14
+ spec: ModelSpec,
15
+ messages: NativeChatMessage[],
16
+ tools?: ToolSchema[],
17
+ media?: CompatMediaOptions,
18
+ ): Record<string, unknown> {
19
+ const body: Record<string, unknown> = {
20
+ model: spec.model,
21
+ messages: renderMessagesWithMedia(messages, media),
22
+ chat_template_kwargs: { enable_thinking: spec.thinking },
23
+ };
24
+ if (spec.thinking && spec.reasoningEffort) body.reasoning_effort = spec.reasoningEffort;
25
+ if (spec.temperature !== undefined) body.temperature = spec.temperature;
26
+ if (spec.maxTokens !== undefined) body.max_tokens = spec.maxTokens;
27
+ const mapped = mapTools(tools);
28
+ if (mapped) body.tools = mapped;
29
+ return body;
30
+ }
31
+
32
+ export class LlamaCppProvider extends OpenAIHttpClient {
33
+ private readonly apiKey?: string;
34
+ private readonly media?: CompatMediaOptions;
35
+
36
+ constructor(opts: { baseUrl: string; apiKey?: string; log?: Logger; media?: CompatMediaOptions }) {
37
+ super(opts.baseUrl, opts.log ?? nullLogger());
38
+ this.apiKey = opts.apiKey;
39
+ this.media = opts.media;
40
+ }
41
+
42
+ protected buildBody(spec: ModelSpec, messages: NativeChatMessage[], tools?: ToolSchema[]): Record<string, unknown> {
43
+ return buildLlamaCppRequestBody(spec, messages, tools, this.media);
44
+ }
45
+
46
+ protected headers(): Record<string, string> {
47
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' };
48
+ if (this.apiKey) headers.Authorization = `Bearer ${this.apiKey}`;
49
+ return headers;
50
+ }
51
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Endpoint options of the llamacpp module, and the upstream release table.
3
+ *
4
+ * `options.runtime` present = managed: Cortico downloads the pinned release and starts
5
+ * llama-server in router mode. Absent = external: the endpoint is a llama-server someone else
6
+ * started, and only the catalog and the probes apply.
7
+ */
8
+ import type { LLMProviderEntry } from '../../core/types.ts';
9
+
10
+ /** Upstream build this module was verified against. Endpoints may pin another tag. */
11
+ export const PINNED_RELEASE = 'b10930';
12
+ const RELEASE_BASE = 'https://github.com/ggml-org/llama.cpp/releases/download';
13
+
14
+ export interface RuntimeOptions {
15
+ release: string;
16
+ backend: string;
17
+ /** A directory holding llama-server and its libraries; when set nothing is downloaded. */
18
+ runtimeDir?: string;
19
+ }
20
+ export interface LaunchOptions {
21
+ contextSize: number;
22
+ nGpuLayers: number;
23
+ parallel: number;
24
+ /** Whitespace-separated extra arguments, appended verbatim. */
25
+ extraArgs: string;
26
+ }
27
+ export interface LlamaCppOptions {
28
+ runtime?: RuntimeOptions;
29
+ launch?: LaunchOptions;
30
+ autoStart?: boolean;
31
+ }
32
+
33
+ export const LAUNCH_DEFAULTS: LaunchOptions = { contextSize: 16384, nGpuLayers: 99, parallel: 1, extraArgs: '' };
34
+
35
+ export function llamacppOptions(entry: LLMProviderEntry): LlamaCppOptions {
36
+ return (entry.options ?? {}) as LlamaCppOptions;
37
+ }
38
+
39
+ /** Fills the release and launch defaults for managed endpoints; external ones carry no such keys. */
40
+ export function normalizeLlamaCpp(entry: LLMProviderEntry): LLMProviderEntry {
41
+ const options = { ...entry.options } as LlamaCppOptions;
42
+ if (options.runtime && typeof options.runtime === 'object') {
43
+ const runtime = options.runtime as Partial<RuntimeOptions>;
44
+ options.runtime = { ...runtime, release: runtime.release ?? PINNED_RELEASE, backend: runtime.backend ?? defaultBackend() };
45
+ if (typeof options.runtime.release === 'string') options.runtime.release = options.runtime.release.trim();
46
+ if (typeof options.runtime.runtimeDir === 'string') options.runtime.runtimeDir = options.runtime.runtimeDir.trim();
47
+ if (options.runtime.runtimeDir === '') delete options.runtime.runtimeDir;
48
+ options.launch = { ...LAUNCH_DEFAULTS, ...(options.launch ?? {}) };
49
+ }
50
+ return { ...entry, options: options as Record<string, unknown> };
51
+ }
52
+
53
+ export interface ReleaseArchive {
54
+ url: string;
55
+ file: string;
56
+ format: 'zip' | 'tgz';
57
+ /** Leading path components to drop; the tarballs wrap everything in `llama-<tag>/`. */
58
+ stripComponents: number;
59
+ }
60
+ export interface ReleasePlan {
61
+ /** Directory name under the release: `win-cuda-13.3-x64`, `ubuntu-vulkan-x64`, `macos-arm64`. */
62
+ key: string;
63
+ archives: ReleaseArchive[];
64
+ /** Server executable name inside the directory. */
65
+ serverExe: string;
66
+ }
67
+
68
+ type Platform = NodeJS.Platform;
69
+ type Arch = string;
70
+
71
+ /** Backends the upstream ships a prebuilt archive for, per platform. First one is the default. */
72
+ export function backendChoices(platform: Platform = process.platform, arch: Arch = process.arch): string[] {
73
+ if (platform === 'win32') {
74
+ if (arch === 'x64') return ['cuda-13.3', 'cuda-12.4', 'vulkan', 'cpu'];
75
+ if (arch === 'arm64') return ['cuda-13.4', 'cpu'];
76
+ return [];
77
+ }
78
+ if (platform === 'linux') {
79
+ if (arch === 'x64') return ['vulkan', 'rocm-10.0', 'cpu'];
80
+ if (arch === 'arm64') return ['vulkan', 'cpu'];
81
+ return [];
82
+ }
83
+ if (platform === 'darwin') return arch === 'x64' || arch === 'arm64' ? ['metal'] : [];
84
+ return [];
85
+ }
86
+
87
+ export function defaultBackend(platform: Platform = process.platform, arch: Arch = process.arch): string {
88
+ return backendChoices(platform, arch)[0] ?? 'cpu';
89
+ }
90
+
91
+ /** The archives that make up one build on one platform; null when the upstream ships none. */
92
+ export function releasePlan(
93
+ release: string,
94
+ backend: string,
95
+ platform: Platform = process.platform,
96
+ arch: Arch = process.arch,
97
+ ): ReleasePlan | null {
98
+ if (!backendChoices(platform, arch).includes(backend)) return null;
99
+ const asset = (file: string, format: 'zip' | 'tgz', stripComponents: number): ReleaseArchive => ({
100
+ url: `${RELEASE_BASE}/${release}/${file}`,
101
+ file,
102
+ format,
103
+ stripComponents,
104
+ });
105
+ if (platform === 'win32') {
106
+ const key = `win-${backend}-${arch}`;
107
+ const archives = [asset(`llama-${release}-bin-${key}.zip`, 'zip', 0)];
108
+ if (backend.startsWith('cuda-')) archives.push(asset(`cudart-llama-bin-win-${backend}-${arch}.zip`, 'zip', 0));
109
+ return { key, archives, serverExe: 'llama-server.exe' };
110
+ }
111
+ if (platform === 'linux') {
112
+ const key = backend === 'cpu' ? `ubuntu-${arch}` : `ubuntu-${backend}-${arch}`;
113
+ return { key, archives: [asset(`llama-${release}-bin-${key}.tar.gz`, 'tgz', 1)], serverExe: 'llama-server' };
114
+ }
115
+ const key = `macos-${arch}`;
116
+ return { key, archives: [asset(`llama-${release}-bin-${key}.tar.gz`, 'tgz', 1)], serverExe: 'llama-server' };
117
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Install builds under <runtimes root>/llama.cpp/<release>/<plan key>/.
3
+ * Download and unpack into <dir>.partial, write the completion marker, then rename to the destination.
4
+ * A later installation removes any remaining partial directory before starting.
5
+ */
6
+ import { existsSync, mkdirSync, readFileSync, rmSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import type { Logger } from '../../core/types.ts';
9
+ import type { Language } from '../../core/language.ts';
10
+ import { downloadFile, extractArchive } from './archive.ts';
11
+ import type { ReleasePlan } from './options.ts';
12
+ import { text, type Text } from './strings.ts';
13
+
14
+ export const RUNTIME_MARKER = 'cortico-runtime.json';
15
+ const RUNTIME_ID = 'llama.cpp';
16
+
17
+ export type InstallPhase = 'absent' | 'downloading' | 'extracting' | 'installed' | 'error';
18
+
19
+ export interface InstallState {
20
+ phase: InstallPhase;
21
+ /** Archive being downloaded or unpacked. */
22
+ file: string | null;
23
+ done: number;
24
+ total: number | null;
25
+ detail: string | null;
26
+ }
27
+
28
+ export interface RuntimeMarker {
29
+ runtime: typeof RUNTIME_ID;
30
+ release: string;
31
+ key: string;
32
+ archives: string[];
33
+ installedAt: string;
34
+ }
35
+
36
+ type Detail = (S: Text) => string;
37
+
38
+ export class RuntimeStore {
39
+ private active: { dir: string; phase: InstallPhase; file: string | null; done: number; total: number | null } | null = null;
40
+ private failures = new Map<string, Detail>();
41
+ private readonly fetchImpl: typeof fetch;
42
+
43
+ constructor(
44
+ private readonly root: string,
45
+ private readonly log: Logger,
46
+ fetchImpl?: typeof fetch,
47
+ ) {
48
+ this.fetchImpl = fetchImpl ?? fetch;
49
+ }
50
+
51
+ dir(release: string, plan: ReleasePlan): string {
52
+ return join(this.root, RUNTIME_ID, release, plan.key);
53
+ }
54
+
55
+ installed(dir: string): boolean {
56
+ return existsSync(join(dir, RUNTIME_MARKER));
57
+ }
58
+
59
+ marker(dir: string): RuntimeMarker | null {
60
+ try {
61
+ return JSON.parse(readFileSync(join(dir, RUNTIME_MARKER), 'utf8')) as RuntimeMarker;
62
+ } catch {
63
+ return null;
64
+ }
65
+ }
66
+
67
+ state(dir: string, language: Language = 'zh'): InstallState {
68
+ if (this.active?.dir === dir) {
69
+ const { phase, file, done, total } = this.active;
70
+ return { phase, file, done, total, detail: null };
71
+ }
72
+ const failure = this.failures.get(dir);
73
+ if (failure) return { phase: 'error', file: null, done: 0, total: null, detail: failure(text(language)) };
74
+ return { phase: this.installed(dir) ? 'installed' : 'absent', file: null, done: 0, total: null, detail: null };
75
+ }
76
+
77
+ /** Downloads and unpacks every archive of the plan; a second concurrent install is refused. */
78
+ async install(release: string, plan: ReleasePlan, language: Language = 'zh'): Promise<void> {
79
+ const dir = this.dir(release, plan);
80
+ if (this.active) throw new Error(text(language).installBusy);
81
+ this.failures.delete(dir);
82
+ const partial = `${dir}.partial`;
83
+ rmSync(partial, { recursive: true, force: true });
84
+ rmSync(dir, { recursive: true, force: true });
85
+ mkdirSync(partial, { recursive: true });
86
+ this.active = { dir, phase: 'downloading', file: null, done: 0, total: null };
87
+ try {
88
+ for (const archive of plan.archives) {
89
+ const file = join(partial, archive.file);
90
+ this.active = { dir, phase: 'downloading', file: archive.file, done: 0, total: null };
91
+ this.log.info(`llama.cpp 运行时下载 ${archive.url}`);
92
+ try {
93
+ await downloadFile(archive.url, file, {
94
+ fetchImpl: this.fetchImpl,
95
+ onProgress: (done, total) => {
96
+ if (this.active?.dir === dir) Object.assign(this.active, { done, total });
97
+ },
98
+ });
99
+ } catch (error) {
100
+ throw { detail: (S: Text) => S.downloadFailed(archive.file, message(error)) };
101
+ }
102
+ this.active = { dir, phase: 'extracting', file: archive.file, done: 0, total: null };
103
+ try {
104
+ await extractArchive(file, archive.format, partial, archive.stripComponents);
105
+ } catch (error) {
106
+ throw { detail: (S: Text) => S.extractFailed(archive.file, message(error)) };
107
+ }
108
+ unlinkSync(file);
109
+ }
110
+ const marker: RuntimeMarker = {
111
+ runtime: RUNTIME_ID,
112
+ release,
113
+ key: plan.key,
114
+ archives: plan.archives.map((archive) => archive.file),
115
+ installedAt: new Date().toISOString(),
116
+ };
117
+ writeFileSync(join(partial, RUNTIME_MARKER), JSON.stringify(marker, null, 2) + '\n');
118
+ mkdirSync(join(dir, '..'), { recursive: true });
119
+ renameSync(partial, dir);
120
+ this.log.info(`llama.cpp 运行时已安装 ${dir}`);
121
+ } catch (error) {
122
+ rmSync(partial, { recursive: true, force: true });
123
+ const detail: Detail = isDetail(error) ? error.detail : () => message(error);
124
+ this.failures.set(dir, detail);
125
+ this.log.warn(`llama.cpp 运行时安装失败: ${detail(text('zh'))}`);
126
+ throw new Error(detail(text(language)));
127
+ } finally {
128
+ this.active = null;
129
+ }
130
+ }
131
+ }
132
+
133
+ function isDetail(value: unknown): value is { detail: Detail } {
134
+ return typeof value === 'object' && value !== null && typeof (value as { detail?: unknown }).detail === 'function';
135
+ }
136
+
137
+ function message(error: unknown): string {
138
+ return error instanceof Error ? error.message : String(error);
139
+ }