wizards 0.2.25 → 0.2.27
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/apps/runtime/dist/agents/architect.js +5 -0
- package/apps/runtime/dist/agents/project-assistant.js +10 -1
- package/apps/runtime/dist/authoring/guide.js +61 -8
- package/apps/runtime/dist/connectors/builtin.js +17 -1
- package/apps/runtime/dist/connectors/index.js +43 -14
- package/apps/runtime/dist/connectors/plugin-connections.js +248 -0
- package/apps/runtime/dist/connectors/via-account.js +63 -0
- package/apps/runtime/dist/credits/estimate.js +12 -2
- package/apps/runtime/dist/db/migrations/tenant/0010_plugin_connection.sql +15 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0010_snapshot.json +2942 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +7 -0
- package/apps/runtime/dist/db/schema.js +21 -0
- package/apps/runtime/dist/engenty/connections-google/connectors/calendar.js +25 -3
- package/apps/runtime/dist/engenty/connections-microsoft/outlook.js +29 -1
- package/apps/runtime/dist/engine/decide.js +82 -0
- package/apps/runtime/dist/engine/input.js +21 -5
- package/apps/runtime/dist/engine/requirements.js +80 -15
- package/apps/runtime/dist/engine/runner.js +252 -6
- package/apps/runtime/dist/engine/steps.js +206 -18
- package/apps/runtime/dist/engine/surface.js +90 -0
- package/apps/runtime/dist/mcp/tools.js +3 -2
- package/apps/runtime/dist/plugins/loader.js +22 -1
- package/apps/runtime/dist/plugins/registry.js +10 -0
- package/apps/runtime/dist/render/downloads.js +7 -0
- package/apps/runtime/dist/routes/runs.js +28 -7
- package/apps/runtime/dist/routes/studio.js +3 -1
- package/apps/runtime/dist/services/marketplace.js +134 -3
- package/apps/runtime/dist/services/relevance.js +17 -49
- package/apps/runtime/dist/services/runs.js +43 -15
- package/apps/runtime/dist/services/wizards.js +4 -2
- package/apps/runtime/dist/tools/pages.js +31 -6
- package/apps/runtime/dist/tools/plugin.js +29 -1
- package/apps/web/dist/assets/Account-DW396ig5.js +1 -0
- package/apps/web/dist/assets/{ChangelogDialog-B7C0r9dF.js → ChangelogDialog-BdZ1SmEd.js} +1 -1
- package/apps/web/dist/assets/CreditsDialog-fOYdoev3.js +1 -0
- package/apps/web/dist/assets/EditorPage-CAARFsaI.js +3 -0
- package/apps/web/dist/assets/{FlowPreview-D0m5Qk6S.js → FlowPreview-U2AEnKlX.js} +1 -1
- package/apps/web/dist/assets/{Harness-bIL3pHB_.js → Harness-CtDlm_2b.js} +2 -2
- package/apps/web/dist/assets/HomePage-Bd-mkLxW.js +1 -0
- package/apps/web/dist/assets/ImportWizard-BMPGjd_t.js +1 -0
- package/apps/web/dist/assets/LocalRuntime-DjSNCRnV.js +1 -0
- package/apps/web/dist/assets/NewWizardPage-DZPcDR9i.js +2 -0
- package/apps/web/dist/assets/ProjectPage-_LAtG3tn.js +2 -0
- package/apps/web/dist/assets/PublicRunner-6Iqp0DmU.js +1 -0
- package/apps/web/dist/assets/RunDrawer-CtQoY3Ha.js +4 -0
- package/apps/web/dist/assets/RunnerView-BLOCNZaZ.js +2 -0
- package/apps/web/dist/assets/SettingsPage-Z6skBf6I.js +1 -0
- package/apps/web/dist/assets/SetupPage-4-Lic4pT.js +1 -0
- package/apps/web/dist/assets/SharePage-DfdhXxZm.js +1 -0
- package/apps/web/dist/assets/arrow-right-BOtqnsA1.js +1 -0
- package/apps/web/dist/assets/brand-CCAI9p0f.js +2 -0
- package/apps/web/dist/assets/changelog-DQM0oz9S.js +1 -0
- package/apps/web/dist/assets/circle-alert-BC6xmJ9r.js +1 -0
- package/apps/web/dist/assets/definition-D5V79jHA.js +1 -0
- package/apps/web/dist/assets/esm-FEWi61F_.js +7 -0
- package/apps/web/dist/assets/external-link-wKL1HE6G.js +1 -0
- package/apps/web/dist/assets/image-BMdSChOR.js +1 -0
- package/apps/web/dist/assets/index-BOp2fz-2.css +1 -0
- package/apps/web/dist/assets/index-YJMooz0J.js +132 -0
- package/apps/web/dist/assets/lock-BpqzLMuv.js +1 -0
- package/apps/web/dist/assets/meta-BqZ7SlZz.js +1 -0
- package/apps/web/dist/assets/pen-line-DM-u0dES.js +1 -0
- package/apps/web/dist/assets/refresh-cw-Bk7HkOIM.js +1 -0
- package/apps/web/dist/assets/settings-menu-BM_NfZfn.js +1 -0
- package/apps/web/dist/assets/star-JHzYYl5R.js +1 -0
- package/apps/web/dist/assets/trash-CdLg-Ela.js +1 -0
- package/apps/web/dist/assets/ui-8QmvuC9u.js +9 -0
- package/apps/web/dist/assets/upload-CL1R6shM.js +1 -0
- package/apps/web/dist/assets/video-BioLmXNO.js +1 -0
- package/apps/web/dist/assets/volume-2-_IOfPNCv.js +1 -0
- package/apps/web/dist/assets/wand-sparkles-DYtDQSJZ.js +1 -0
- package/apps/web/dist/index.html +4 -5
- package/apps/web/dist/mcp/flow.html +61 -61
- package/node_modules/@engenty-wizards/plugin-sdk/dist/index.d.ts +85 -0
- package/node_modules/@engenty-wizards/plugin-sdk/dist/studio.d.ts +23 -0
- package/node_modules/@engenty-wizards/shared/dist/decision.d.ts +76 -0
- package/node_modules/@engenty-wizards/shared/dist/decision.js +147 -0
- package/node_modules/@engenty-wizards/shared/dist/definition.d.ts +1076 -51
- package/node_modules/@engenty-wizards/shared/dist/definition.js +597 -44
- package/node_modules/@engenty-wizards/shared/dist/marketplace-entry.js +19 -2
- package/node_modules/@engenty-wizards/shared/dist/marketplace.d.ts +6 -1
- package/node_modules/@engenty-wizards/shared/dist/run.d.ts +55 -0
- package/node_modules/@engenty-wizards/shared/dist/run.js +3 -0
- package/node_modules/@engenty-wizards/shared/dist/surface.d.ts +89 -0
- package/node_modules/@engenty-wizards/shared/dist/surface.js +198 -0
- package/package.json +1 -1
- package/apps/web/dist/assets/Account-CpxsROpe.js +0 -1
- package/apps/web/dist/assets/CreditsDialog-Com2Y3g8.js +0 -1
- package/apps/web/dist/assets/EditorPage-BCagWjrz.js +0 -2
- package/apps/web/dist/assets/HomePage-DPE_Z9HB.js +0 -1
- package/apps/web/dist/assets/ImportWizard-Dz2uKznp.js +0 -1
- package/apps/web/dist/assets/LocalRuntime-wMKUVZOu.js +0 -1
- package/apps/web/dist/assets/NewWizardPage-BOxU2jWq.js +0 -2
- package/apps/web/dist/assets/ProjectPage-BJp2wqIk.js +0 -5
- package/apps/web/dist/assets/PublicRunner-BsrqVK0J.js +0 -1
- package/apps/web/dist/assets/RunDrawer-CLfJ0W_V.js +0 -1
- package/apps/web/dist/assets/RunnerView-BXMELFC4.js +0 -70
- package/apps/web/dist/assets/SettingsPage-BkuUm-bf.js +0 -1
- package/apps/web/dist/assets/SetupPage-CoyXpcvS.js +0 -1
- package/apps/web/dist/assets/SharePage-BuR-he1L.js +0 -1
- package/apps/web/dist/assets/api-rJBQEu1G.js +0 -10
- package/apps/web/dist/assets/arrow-right-6FyN3e7D.js +0 -1
- package/apps/web/dist/assets/changelog-Bmw6dg-T.js +0 -1
- package/apps/web/dist/assets/check-CUpby_J9.js +0 -1
- package/apps/web/dist/assets/chevron-down-DymW-2BP.js +0 -1
- package/apps/web/dist/assets/circle-alert-D2SCwFT-.js +0 -1
- package/apps/web/dist/assets/createLucideIcon-q3MZMmnf.js +0 -1
- package/apps/web/dist/assets/definition-BznNdlhf.js +0 -55
- package/apps/web/dist/assets/esm-BrGYYg2h.js +0 -7
- package/apps/web/dist/assets/external-link-MAL7vJYj.js +0 -1
- package/apps/web/dist/assets/image-CC_2mJd6.js +0 -1
- package/apps/web/dist/assets/index-BZs1IxAk.css +0 -1
- package/apps/web/dist/assets/index-Co5QOA9_.js +0 -10
- package/apps/web/dist/assets/lock-DZL2N67v.js +0 -1
- package/apps/web/dist/assets/meta-B3jzXP27.js +0 -1
- package/apps/web/dist/assets/pen-line-NK46C_nG.js +0 -1
- package/apps/web/dist/assets/plus-Du-Jx8aI.js +0 -1
- package/apps/web/dist/assets/refresh-cw-BNtHis1x.js +0 -1
- package/apps/web/dist/assets/settings-menu-CCNUb0Fd.js +0 -1
- package/apps/web/dist/assets/table-2-BdDuBcF2.js +0 -1
- package/apps/web/dist/assets/trash-0IafyhZB.js +0 -1
- package/apps/web/dist/assets/ui-DJOZSSKo.js +0 -1
- package/apps/web/dist/assets/upload-DNRENWzQ.js +0 -1
- package/apps/web/dist/assets/video-9fDPRPCv.js +0 -1
- package/apps/web/dist/assets/volume-2-DE67_rue.js +0 -1
- package/apps/web/dist/assets/wand-sparkles-Clj3VRB5.js +0 -1
|
@@ -77,8 +77,11 @@ export interface PluginStepContext {
|
|
|
77
77
|
name: string;
|
|
78
78
|
};
|
|
79
79
|
wizard: {
|
|
80
|
+
id: string;
|
|
80
81
|
title: string;
|
|
81
82
|
};
|
|
83
|
+
/** "test": a test run from the studio. Keep what it makes apart from what real runs make. */
|
|
84
|
+
mode: "test" | "live";
|
|
82
85
|
/** Aborted when the run is cancelled. */
|
|
83
86
|
signal: AbortSignal;
|
|
84
87
|
/** Tells the person what the step is doing right now. */
|
|
@@ -453,6 +456,82 @@ export interface PluginPublicRoute {
|
|
|
453
456
|
path: string;
|
|
454
457
|
handler(request: PluginPublicRequest): unknown | Promise<unknown>;
|
|
455
458
|
}
|
|
459
|
+
/** An account a plugin connected for a space: a calendar, a mailbox. */
|
|
460
|
+
export interface PluginConnection {
|
|
461
|
+
id: string;
|
|
462
|
+
/** The space it was connected for. */
|
|
463
|
+
space: string;
|
|
464
|
+
/** The built-in connector behind it: `google-calendar`, `microsoft-outlook`. */
|
|
465
|
+
connector: string;
|
|
466
|
+
/** The account, as the provider names it: an e-mail address. */
|
|
467
|
+
label: string;
|
|
468
|
+
/** The actions it was connected for; the provider granted what they need. */
|
|
469
|
+
actions: string[];
|
|
470
|
+
createdAt: string;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Accounts a plugin connects on its own, for a space rather than for a wizard's run: the
|
|
474
|
+
* runtime's built-in connectors (Google, Microsoft, …), signed in with OAuth. The runtime keeps
|
|
475
|
+
* the tokens sealed, refreshes them, and on a local install connects Google through the linked
|
|
476
|
+
* account's Manage-App, as it does for wizards.
|
|
477
|
+
*/
|
|
478
|
+
export interface PluginConnections {
|
|
479
|
+
/** Of these connector ids, the ones a person can connect on this runtime now, with their names. */
|
|
480
|
+
available(connectors: string[]): Promise<{
|
|
481
|
+
id: string;
|
|
482
|
+
name: string;
|
|
483
|
+
}[]>;
|
|
484
|
+
/**
|
|
485
|
+
* The provider's sign-in page. Open it in a window: when the person is done, the window posts
|
|
486
|
+
* `{ type: "wizards:connected", ok }` to the studio and closes. `actions` are the connector's
|
|
487
|
+
* actions the plugin will call; only the rights they need are asked for.
|
|
488
|
+
*/
|
|
489
|
+
start(input: {
|
|
490
|
+
space: string;
|
|
491
|
+
connector: string;
|
|
492
|
+
actions: string[];
|
|
493
|
+
}): Promise<{
|
|
494
|
+
url: string;
|
|
495
|
+
}>;
|
|
496
|
+
/** What is connected for the space, the newest first. */
|
|
497
|
+
list(space: string): Promise<PluginConnection[]>;
|
|
498
|
+
/** One connection by its id, or null. */
|
|
499
|
+
get(connectionId: string): Promise<PluginConnection | null>;
|
|
500
|
+
/**
|
|
501
|
+
* Runs one of the connector's actions with the connection's account. Throws when the action
|
|
502
|
+
* is not one the connection was made for, or when the provider refuses (the account was
|
|
503
|
+
* disconnected there: connect again).
|
|
504
|
+
*/
|
|
505
|
+
call<T = unknown>(connectionId: string, action: string, input: Record<string, unknown>): Promise<T>;
|
|
506
|
+
/** Forgets the connection and its tokens. */
|
|
507
|
+
remove(connectionId: string): Promise<void>;
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* A wizard a plugin brings as a template: offered beside the marketplace's starters when a
|
|
511
|
+
* wizard is made, and to the assistant and AI apps as an example to copy.
|
|
512
|
+
*/
|
|
513
|
+
export interface PluginStarter {
|
|
514
|
+
/** Lower case, digits and `-`; the starter is known as `<plugin id>.<id>`. */
|
|
515
|
+
id: string;
|
|
516
|
+
title: string;
|
|
517
|
+
/** What it makes, in one or two sentences. */
|
|
518
|
+
description: string;
|
|
519
|
+
/** The language its texts are in. Default: German. */
|
|
520
|
+
language?: "de" | "en";
|
|
521
|
+
/** A complete wizard definition. Checked when it is registered. */
|
|
522
|
+
definition: unknown;
|
|
523
|
+
/** Workspace files it needs (widgets, sample data), by path. */
|
|
524
|
+
files?: Record<string, string | Uint8Array>;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* What the space assistant is told when the admin talks to it from the plugin's own page (the
|
|
528
|
+
* studio half's `studio.Assistant`): what the page holds now, so it can change it with the
|
|
529
|
+
* plugin's assistant tools.
|
|
530
|
+
*/
|
|
531
|
+
export interface PluginAssistantPage {
|
|
532
|
+
/** Text for the assistant: the state of the page, in a few lines. Cut at 8,000 characters. */
|
|
533
|
+
block(space: PluginSpace): string | null | Promise<string | null>;
|
|
534
|
+
}
|
|
456
535
|
export interface PluginServerApi {
|
|
457
536
|
registerHttpRoute(route: PluginRoute): void;
|
|
458
537
|
registerTool<S extends z.ZodType>(tool: PluginTool<S>): void;
|
|
@@ -460,6 +539,8 @@ export interface PluginServerApi {
|
|
|
460
539
|
registerSpaceContext(context: PluginSpaceContext): void;
|
|
461
540
|
/** A tool of the space assistant. */
|
|
462
541
|
registerAssistantTool<S extends z.ZodType>(tool: PluginAssistantTool<S>): void;
|
|
542
|
+
/** What the space assistant is told when the admin talks to it from the plugin's own page. */
|
|
543
|
+
registerAssistantContext(context: PluginAssistantPage): void;
|
|
463
544
|
registerPublicRoute(route: PluginPublicRoute): void;
|
|
464
545
|
/** The full address of a public route for the current tenant: what to hand to whoever calls it. */
|
|
465
546
|
publicUrl(path: string): Promise<string>;
|
|
@@ -491,6 +572,10 @@ export interface PluginServerApi {
|
|
|
491
572
|
* status 503 and the code `no_model`, which a route passes on as it is.
|
|
492
573
|
*/
|
|
493
574
|
generate<T = undefined>(request: PluginGenerateRequest<T>): Promise<PluginGenerateResult<T>>;
|
|
575
|
+
/** Accounts the plugin connects for a space, through the runtime's built-in connectors. */
|
|
576
|
+
connections: PluginConnections;
|
|
577
|
+
/** A wizard the plugin brings as a template. */
|
|
578
|
+
registerStarter(starter: PluginStarter): void;
|
|
494
579
|
/** Something to undo when the plugin unloads or reloads: a timer, a socket. */
|
|
495
580
|
onUnload(dispose: () => void | Promise<void>): void;
|
|
496
581
|
}
|
|
@@ -80,6 +80,15 @@ export interface StudioSpace {
|
|
|
80
80
|
/** Synced from a local install, or on a runtime where nothing is built: shown, not changed. */
|
|
81
81
|
readOnly: boolean;
|
|
82
82
|
}
|
|
83
|
+
/** The space assistant on a plugin's page: what it says before the first message. */
|
|
84
|
+
export interface StudioAssistantProps {
|
|
85
|
+
title?: string;
|
|
86
|
+
hello?: string;
|
|
87
|
+
/** The input's placeholder: an example of what to ask. */
|
|
88
|
+
placeholder?: string;
|
|
89
|
+
/** The assistant changed something: read the page's data again. */
|
|
90
|
+
onChanged?: () => void;
|
|
91
|
+
}
|
|
83
92
|
export interface StudioMe {
|
|
84
93
|
user: {
|
|
85
94
|
id: string;
|
|
@@ -109,6 +118,20 @@ export interface StudioPluginContext {
|
|
|
109
118
|
* again when the person picks another space.
|
|
110
119
|
*/
|
|
111
120
|
useSpace(): StudioSpace | null;
|
|
121
|
+
/**
|
|
122
|
+
* A hook: where the page stands, as the trail after the logo in the top bar ("Termine /
|
|
123
|
+
* Kalender"), said there instead of a heading of the page's own. Set while the page is shown.
|
|
124
|
+
*/
|
|
125
|
+
useCrumbs(items: {
|
|
126
|
+
label: string;
|
|
127
|
+
to?: string;
|
|
128
|
+
}[]): void;
|
|
129
|
+
/**
|
|
130
|
+
* The space assistant, docked at the lower edge of a page of the plugin, as on the space page.
|
|
131
|
+
* It talks from the plugin's page: it gets what the server half's `registerAssistantContext`
|
|
132
|
+
* says and uses the plugin's assistant tools first. Place it once, anywhere in the page.
|
|
133
|
+
*/
|
|
134
|
+
Assistant: ComponentType<StudioAssistantProps>;
|
|
112
135
|
i18n: {
|
|
113
136
|
/** Takes the plugin's words and gives the function that reads them in the language in effect. */
|
|
114
137
|
register<M extends StudioMessages>(messages: M): (key: keyof M["de"] & string, vars?: Record<string, string | number>) => string;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Decisions: typed questions over a state, answered by a decision model (System One: TypeSafe's
|
|
4
|
+
* Jev and models of its kind) or, where there is none, by a language model asked for the same
|
|
5
|
+
* answers as structured output. Both ways give the same shapes; only a decision model gives
|
|
6
|
+
* probabilities that mean something.
|
|
7
|
+
*/
|
|
8
|
+
/** Yes or no: P(yes). */
|
|
9
|
+
export interface NoulQuestion {
|
|
10
|
+
type: "noul";
|
|
11
|
+
instructions: string;
|
|
12
|
+
criteria?: {
|
|
13
|
+
true: string;
|
|
14
|
+
false: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** One of named options, each with what it covers (null: the name says it). */
|
|
18
|
+
export interface ChoiceQuestion {
|
|
19
|
+
type: "choice";
|
|
20
|
+
instructions: string;
|
|
21
|
+
criteria: Record<string, string | null>;
|
|
22
|
+
}
|
|
23
|
+
/** A level on an ordered scale, lowest first. */
|
|
24
|
+
export interface ScoreQuestion {
|
|
25
|
+
type: "score";
|
|
26
|
+
instructions: string;
|
|
27
|
+
criteria: string[];
|
|
28
|
+
}
|
|
29
|
+
export type DecisionQuestion = NoulQuestion | ChoiceQuestion | ScoreQuestion;
|
|
30
|
+
export interface NoulAnswer {
|
|
31
|
+
type: "noul";
|
|
32
|
+
/** P(yes), 0–1. A language model's answer is 1 or 0. */
|
|
33
|
+
noul: number;
|
|
34
|
+
}
|
|
35
|
+
export interface ChoiceAnswer {
|
|
36
|
+
type: "choice";
|
|
37
|
+
choice: string;
|
|
38
|
+
/** Per option; only from a decision model. */
|
|
39
|
+
probabilities?: Record<string, number>;
|
|
40
|
+
confidence?: number;
|
|
41
|
+
}
|
|
42
|
+
export interface ScoreAnswer {
|
|
43
|
+
type: "score";
|
|
44
|
+
/** Index of the level, lowest = 0. */
|
|
45
|
+
level: number;
|
|
46
|
+
/** The level's description, as the question lists it. */
|
|
47
|
+
label: string;
|
|
48
|
+
/** Per level index; only from a decision model. */
|
|
49
|
+
probabilities?: Record<string, number>;
|
|
50
|
+
confidence?: number;
|
|
51
|
+
}
|
|
52
|
+
export type DecisionAnswer = NoulAnswer | ChoiceAnswer | ScoreAnswer;
|
|
53
|
+
export type DecisionSource = "systemone" | "llm";
|
|
54
|
+
export interface Decision {
|
|
55
|
+
answers: Record<string, DecisionAnswer>;
|
|
56
|
+
/** Which way answered; with both, the language model filled what the decision model left open. */
|
|
57
|
+
source: DecisionSource;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The answers a System One response gives that fit their questions; a question whose answer is
|
|
61
|
+
* missing or does not fit is left out, for the language model to answer.
|
|
62
|
+
*/
|
|
63
|
+
export declare function readSystemOne(questions: Record<string, DecisionQuestion>, response: unknown): Record<string, DecisionAnswer>;
|
|
64
|
+
/** The questions as one structured-output object a language model fills. */
|
|
65
|
+
export declare function decisionSchema(questions: Record<string, DecisionQuestion>): z.ZodObject<{
|
|
66
|
+
[x: string]: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
67
|
+
}, z.core.$strip>;
|
|
68
|
+
/** What a language model filled in, as answers; null when it does not fit. */
|
|
69
|
+
export declare function readObject(questions: Record<string, DecisionQuestion>, object: unknown): Record<string, DecisionAnswer> | null;
|
|
70
|
+
/**
|
|
71
|
+
* How sure the answer is that it picked rightly, as the margin between the winner and the
|
|
72
|
+
* runner-up; null where there are no probabilities (a language model's answer).
|
|
73
|
+
*/
|
|
74
|
+
export declare function marginOf(answer: DecisionAnswer): number | null;
|
|
75
|
+
/** The probability of what was answered: P(yes) for a yes/no, P(pick) otherwise; 1 without. */
|
|
76
|
+
export declare function pOf(answer: DecisionAnswer): number;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Probabilities sum to one within rounding. */
|
|
3
|
+
const SUM_TOLERANCE = 0.02;
|
|
4
|
+
function unit(v) {
|
|
5
|
+
return typeof v === "number" && Number.isFinite(v) && v >= 0 && v <= 1;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A distribution over exactly the offered keys that sums to one, with `pick` on top — so a
|
|
9
|
+
* malformed or cut-off answer never counts. Engenty-pro's validateChoiceAnswer.
|
|
10
|
+
*/
|
|
11
|
+
function validDistribution(probabilities, offered, pick) {
|
|
12
|
+
if (!probabilities || typeof probabilities !== "object") {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
const p = probabilities;
|
|
16
|
+
const keys = Object.keys(p);
|
|
17
|
+
if (keys.length !== offered.length || !offered.every((k) => k in p)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
let sum = 0;
|
|
21
|
+
let max = 0;
|
|
22
|
+
for (const k of keys) {
|
|
23
|
+
if (!unit(p[k])) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
sum += p[k];
|
|
27
|
+
max = Math.max(max, p[k]);
|
|
28
|
+
}
|
|
29
|
+
return Math.abs(sum - 1) < SUM_TOLERANCE && p[pick] >= max - 1e-6;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The answers a System One response gives that fit their questions; a question whose answer is
|
|
33
|
+
* missing or does not fit is left out, for the language model to answer.
|
|
34
|
+
*/
|
|
35
|
+
export function readSystemOne(questions, response) {
|
|
36
|
+
const raw = response?.answers ?? {};
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const [name, q] of Object.entries(questions)) {
|
|
39
|
+
const a = raw[name];
|
|
40
|
+
if (!a || typeof a !== "object") {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (q.type === "noul" && a.type === "noul" && unit(a.noul)) {
|
|
44
|
+
out[name] = { type: "noul", noul: a.noul };
|
|
45
|
+
}
|
|
46
|
+
else if (q.type === "choice" && a.type === "choice") {
|
|
47
|
+
const offered = Object.keys(q.criteria);
|
|
48
|
+
if (typeof a.choice === "string" &&
|
|
49
|
+
offered.includes(a.choice) &&
|
|
50
|
+
validDistribution(a.probabilities, offered, a.choice)) {
|
|
51
|
+
out[name] = {
|
|
52
|
+
type: "choice",
|
|
53
|
+
choice: a.choice,
|
|
54
|
+
probabilities: a.probabilities,
|
|
55
|
+
...(unit(a.confidence) ? { confidence: a.confidence } : {}),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (q.type === "score" && a.type === "score") {
|
|
60
|
+
const offered = q.criteria.map((_, i) => String(i));
|
|
61
|
+
const probabilities = a.probabilities;
|
|
62
|
+
const top = probabilities
|
|
63
|
+
? Object.entries(probabilities).sort((x, y) => y[1] - x[1])[0]?.[0]
|
|
64
|
+
: undefined;
|
|
65
|
+
if (top !== undefined && validDistribution(probabilities, offered, top)) {
|
|
66
|
+
const level = Number(top);
|
|
67
|
+
out[name] = {
|
|
68
|
+
type: "score",
|
|
69
|
+
level,
|
|
70
|
+
label: q.criteria[level],
|
|
71
|
+
probabilities,
|
|
72
|
+
...(unit(a.confidence) ? { confidence: a.confidence } : {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
/** The questions as one structured-output object a language model fills. */
|
|
80
|
+
export function decisionSchema(questions) {
|
|
81
|
+
const shape = {};
|
|
82
|
+
for (const [name, q] of Object.entries(questions)) {
|
|
83
|
+
if (q.type === "noul") {
|
|
84
|
+
const hint = q.criteria ? ` (true: ${q.criteria.true}; false: ${q.criteria.false})` : "";
|
|
85
|
+
shape[name] = z.boolean().describe(`${q.instructions}${hint}`);
|
|
86
|
+
}
|
|
87
|
+
else if (q.type === "choice") {
|
|
88
|
+
const options = Object.keys(q.criteria);
|
|
89
|
+
const hints = Object.entries(q.criteria)
|
|
90
|
+
.filter(([, d]) => d)
|
|
91
|
+
.map(([k, d]) => `${k}: ${d}`)
|
|
92
|
+
.join("; ");
|
|
93
|
+
shape[name] = z
|
|
94
|
+
.enum(options)
|
|
95
|
+
.describe(hints ? `${q.instructions} (${hints})` : q.instructions);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
shape[name] = z
|
|
99
|
+
.enum(q.criteria)
|
|
100
|
+
.describe(`${q.instructions} (lowest to highest)`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return z.object(shape);
|
|
104
|
+
}
|
|
105
|
+
/** What a language model filled in, as answers; null when it does not fit. */
|
|
106
|
+
export function readObject(questions, object) {
|
|
107
|
+
if (!object || typeof object !== "object") {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const o = object;
|
|
111
|
+
const out = {};
|
|
112
|
+
for (const [name, q] of Object.entries(questions)) {
|
|
113
|
+
const v = o[name];
|
|
114
|
+
if (q.type === "noul" && typeof v === "boolean") {
|
|
115
|
+
out[name] = { type: "noul", noul: v ? 1 : 0 };
|
|
116
|
+
}
|
|
117
|
+
else if (q.type === "choice" && typeof v === "string" && v in q.criteria) {
|
|
118
|
+
out[name] = { type: "choice", choice: v };
|
|
119
|
+
}
|
|
120
|
+
else if (q.type === "score" && typeof v === "string" && q.criteria.includes(v)) {
|
|
121
|
+
out[name] = { type: "score", level: q.criteria.indexOf(v), label: v };
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* How sure the answer is that it picked rightly, as the margin between the winner and the
|
|
131
|
+
* runner-up; null where there are no probabilities (a language model's answer).
|
|
132
|
+
*/
|
|
133
|
+
export function marginOf(answer) {
|
|
134
|
+
if (answer.type === "noul") {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
const p = Object.values(answer.probabilities ?? {}).sort((a, b) => b - a);
|
|
138
|
+
return p.length >= 2 ? p[0] - p[1] : null;
|
|
139
|
+
}
|
|
140
|
+
/** The probability of what was answered: P(yes) for a yes/no, P(pick) otherwise; 1 without. */
|
|
141
|
+
export function pOf(answer) {
|
|
142
|
+
if (answer.type === "noul") {
|
|
143
|
+
return answer.noul;
|
|
144
|
+
}
|
|
145
|
+
const key = answer.type === "choice" ? answer.choice : String(answer.level);
|
|
146
|
+
return answer.probabilities?.[key] ?? 1;
|
|
147
|
+
}
|