@byokit/decide 0.1.1 → 0.2.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.
package/README.md CHANGED
@@ -28,6 +28,11 @@ if (intent.abstained) askThePerson(); else route(intent.answer);
28
28
  - **Backends** are tried in order for the questions still unanswered. A backend that fails or takes longer than
29
29
  `timeoutMs` (default 5 s) answers nothing. `privacy: 'stays-here'` skips every backend the state would leave the
30
30
  device for (Jev), so private text never goes to one.
31
+ - **Any model**: `answerer({ name, leaves, ask })` makes a backend of any `(prompt, signal) => text`: on a phone, the
32
+ ChatGPT the person signed in to (`(p, signal) => accounts.respond(me, { instructions: 'Reply with JSON only.',
33
+ input: p, signal })`). It asks for each answer's probability as JSON; any other reply is an abstain.
34
+ - **Phones and browsers**: the main entry is plain TypeScript with `fetch` (the eval CLI is its own entry), so it
35
+ bundles for React Native and the web; `test/react-native.test.ts` runs it where there is no Node.
31
36
  - **Keys**: `jev()` takes the key your host read from its own environment or config. The kit never reads an environment
32
37
  variable, and the key goes only into the one request header. Never ship a key inside an app: keep it on the home
33
38
  computer and let paired devices ask it.
package/dist/index.d.ts CHANGED
@@ -57,3 +57,12 @@ export declare function decide(state: unknown, questions: Record<string, Questio
57
57
  export declare function resolve(q: Question, raw: Raw | undefined): Omit<Answer, 'by' | 'ms'>;
58
58
  /** The app's own function as a backend: return the answer when the case is obvious, undefined otherwise. Stays here. */
59
59
  export declare function rules(fn: (state: any, name: string, q: Question) => string | boolean | number | undefined): Backend;
60
+ /** Any model as a backend: `ask` gets one prompt and returns the model's text (on a phone, the signed-in ChatGPT:
61
+ * `(p, signal) => accounts.respond(me, { instructions: '', input: p, signal })`). The model is asked for each option's
62
+ * probability as JSON; an answer that isn't that JSON is no answer, so the question abstains. `leaves`: whether the
63
+ * state goes off this device (true for any hosted model). */
64
+ export declare function answerer(o: {
65
+ name: string;
66
+ leaves: boolean;
67
+ ask: (prompt: string, signal: AbortSignal) => Promise<string>;
68
+ }): Backend;
package/dist/index.js CHANGED
@@ -95,3 +95,35 @@ export function rules(fn) {
95
95
  },
96
96
  };
97
97
  }
98
+ /** Any model as a backend: `ask` gets one prompt and returns the model's text (on a phone, the signed-in ChatGPT:
99
+ * `(p, signal) => accounts.respond(me, { instructions: '', input: p, signal })`). The model is asked for each option's
100
+ * probability as JSON; an answer that isn't that JSON is no answer, so the question abstains. `leaves`: whether the
101
+ * state goes off this device (true for any hosted model). */
102
+ export function answerer(o) {
103
+ return {
104
+ name: o.name,
105
+ leaves: o.leaves,
106
+ async ask(state, questions, signal) {
107
+ const described = Object.fromEntries(Object.entries(questions).map(([k, q]) => [k,
108
+ q.kind === 'choice' ? { pick_one_of: q.options, instructions: q.instructions }
109
+ : q.kind === 'yesno' ? { yes_or_no: q.question, yes: q.yes, no: q.no, answer_keys: ['true', 'false'] }
110
+ : { rate_on: Object.fromEntries(q.levels.map((l, i) => [String(i), l])), instructions: q.instructions }]));
111
+ const prompt = 'Answer each question about the state below. For each question give every answer key a probability ' +
112
+ 'between 0 and 1, summing to 1. Reply with JSON only, shaped {"<question>": {"<answer key>": <probability>}}.\n\n' +
113
+ `State: ${JSON.stringify(state)}\n\nQuestions: ${JSON.stringify(described)}`;
114
+ const text = await o.ask(prompt, signal);
115
+ let parsed;
116
+ try {
117
+ parsed = JSON.parse(text.trim());
118
+ }
119
+ catch {
120
+ return {};
121
+ }
122
+ const out = Object.create(null);
123
+ for (const k of Object.keys(questions))
124
+ if (parsed?.[k] && typeof parsed[k] === 'object')
125
+ out[k] = { probabilities: parsed[k] };
126
+ return out;
127
+ },
128
+ };
129
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@byokit/decide",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Typed questions in, a typed answer with confidence out, abstaining below a floor. Rules and Jev backends, and an eval runner.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",