@procedures/llm 0.0.0-stage → 0.3.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 (60) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENSE +21 -0
  3. package/README.md +109 -2
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/anthropic.d.ts +102 -0
  6. package/dist/anthropic.d.ts.map +1 -0
  7. package/dist/anthropic.js +195 -0
  8. package/dist/anthropic.js.map +1 -0
  9. package/dist/errors.d.ts +34 -0
  10. package/dist/errors.d.ts.map +1 -0
  11. package/dist/errors.js +20 -0
  12. package/dist/errors.js.map +1 -0
  13. package/dist/index.d.ts +7 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +9 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/module.d.ts +48 -0
  18. package/dist/module.d.ts.map +1 -0
  19. package/dist/module.js +145 -0
  20. package/dist/module.js.map +1 -0
  21. package/dist/procedures/llm/complete.d.ts +13 -0
  22. package/dist/procedures/llm/complete.d.ts.map +1 -0
  23. package/dist/procedures/llm/complete.js +23 -0
  24. package/dist/procedures/llm/complete.js.map +1 -0
  25. package/dist/procedures/llm/index.d.ts +3 -0
  26. package/dist/procedures/llm/index.d.ts.map +1 -0
  27. package/dist/procedures/llm/index.js +3 -0
  28. package/dist/procedures/llm/index.js.map +1 -0
  29. package/dist/procedures/llm/structured.d.ts +16 -0
  30. package/dist/procedures/llm/structured.d.ts.map +1 -0
  31. package/dist/procedures/llm/structured.js +75 -0
  32. package/dist/procedures/llm/structured.js.map +1 -0
  33. package/dist/register.d.ts +6 -0
  34. package/dist/register.d.ts.map +1 -0
  35. package/dist/register.js +8 -0
  36. package/dist/register.js.map +1 -0
  37. package/dist/request.d.ts +10 -0
  38. package/dist/request.d.ts.map +1 -0
  39. package/dist/request.js +26 -0
  40. package/dist/request.js.map +1 -0
  41. package/dist/schema.d.ts +31 -0
  42. package/dist/schema.d.ts.map +1 -0
  43. package/dist/schema.js +86 -0
  44. package/dist/schema.js.map +1 -0
  45. package/dist/types.d.ts +76 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +8 -0
  48. package/dist/types.js.map +1 -0
  49. package/package.json +77 -3
  50. package/src/anthropic.ts +255 -0
  51. package/src/errors.ts +38 -0
  52. package/src/index.ts +20 -0
  53. package/src/module.ts +186 -0
  54. package/src/procedures/llm/complete.ts +26 -0
  55. package/src/procedures/llm/index.ts +2 -0
  56. package/src/procedures/llm/structured.ts +79 -0
  57. package/src/register.ts +10 -0
  58. package/src/request.ts +25 -0
  59. package/src/schema.ts +85 -0
  60. package/src/types.ts +90 -0
package/src/schema.ts ADDED
@@ -0,0 +1,85 @@
1
+ /**
2
+ * The JSON Schema of `llm.structured`: the schema that the API gets, and the parse of an answer.
3
+ *
4
+ * The structured outputs of the API support a subset of JSON Schema. The API refuses the
5
+ * numerical constraints, the string lengths, the array constraints and an `additionalProperties`
6
+ * other than `false`. `constrainedSchema()` removes those keywords, as the Anthropic SDKs do. The
7
+ * procedure examines the full schema after the answer, so the removed keywords still apply.
8
+ */
9
+
10
+ /** The keywords that the structured outputs of the API do not support. */
11
+ export const UNSUPPORTED_KEYWORDS: readonly string[] = Object.freeze([
12
+ "minimum",
13
+ "maximum",
14
+ "exclusiveMinimum",
15
+ "exclusiveMaximum",
16
+ "multipleOf",
17
+ "minLength",
18
+ "maxLength",
19
+ "minItems",
20
+ "maxItems",
21
+ "uniqueItems",
22
+ "contains",
23
+ "minContains",
24
+ "maxContains",
25
+ "minProperties",
26
+ "maxProperties",
27
+ ]);
28
+
29
+ /** The keywords whose value is one schema. */
30
+ const ONE_SCHEMA = ["items", "additionalItems", "not", "if", "then", "else", "propertyNames"];
31
+ /** The keywords whose value is a list of schemas. */
32
+ const SCHEMA_LIST = ["anyOf", "allOf", "oneOf", "prefixItems", "items"];
33
+ /** The keywords whose value is a map of schemas. */
34
+ const SCHEMA_MAP = ["properties", "patternProperties", "$defs", "definitions"];
35
+
36
+ const isRecord = (value: unknown): value is Record<string, unknown> => value !== null && typeof value === "object" && !Array.isArray(value);
37
+
38
+ /** True when a schema describes an object. */
39
+ function isObjectSchema(schema: Record<string, unknown>): boolean {
40
+ const type = schema["type"];
41
+ return type === "object" || (Array.isArray(type) && type.includes("object")) || (type === undefined && isRecord(schema["properties"]));
42
+ }
43
+
44
+ /**
45
+ * This function gives a copy of a schema for the structured outputs of the API: without the
46
+ * unsupported keywords, and with `additionalProperties: false` on each object. Without its
47
+ * constraints, the copy can accept a value that the full schema refuses. Thus the procedure
48
+ * examines each answer against the full schema.
49
+ */
50
+ export function constrainedSchema(schema: unknown): unknown {
51
+ if (Array.isArray(schema)) return schema.map(constrainedSchema);
52
+ if (!isRecord(schema)) return schema;
53
+ const copy: Record<string, unknown> = {};
54
+ for (const [keyword, value] of Object.entries(schema)) {
55
+ if (UNSUPPORTED_KEYWORDS.includes(keyword)) continue;
56
+ if (SCHEMA_MAP.includes(keyword) && isRecord(value)) {
57
+ copy[keyword] = Object.fromEntries(Object.entries(value).map(([name, sub]) => [name, constrainedSchema(sub)]));
58
+ } else if (SCHEMA_LIST.includes(keyword) || ONE_SCHEMA.includes(keyword)) {
59
+ copy[keyword] = constrainedSchema(value);
60
+ } else {
61
+ copy[keyword] = value;
62
+ }
63
+ }
64
+ if (isObjectSchema(copy)) copy["additionalProperties"] = false;
65
+ return copy;
66
+ }
67
+
68
+ /** The result of the parse of an answer. */
69
+ export type ParsedAnswer = { readonly ok: true; readonly value: unknown } | { readonly ok: false; readonly problem: string };
70
+
71
+ /**
72
+ * This function parses the text of an answer as one JSON value. It removes a code fence around
73
+ * the value (` ```json ... ``` `), because a model can add one without the constraint.
74
+ */
75
+ export function parseAnswer(text: string): ParsedAnswer {
76
+ let body = text.trim();
77
+ const fence = /^```[a-zA-Z0-9_-]*\s*\n([\s\S]*?)\n?```$/.exec(body);
78
+ if (fence) body = fence[1]!.trim();
79
+ if (body === "") return { ok: false, problem: "the answer is empty" };
80
+ try {
81
+ return { ok: true, value: JSON.parse(body) as unknown };
82
+ } catch (error) {
83
+ return { ok: false, problem: `the answer is not JSON (${error instanceof Error ? error.message : String(error)})` };
84
+ }
85
+ }
package/src/types.ts ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Type definitions of the procedures of this package
3
+ *
4
+ * mark procedure new adds the types of each new procedure here. The zod schemas are in
5
+ * `module.ts`: an exported schema needs a written type (isolatedDeclarations).
6
+ */
7
+
8
+ /** The effort of the model: how much it thinks and how many tokens it spends. */
9
+ export type LlmEffort = "low" | "medium" | "high" | "xhigh" | "max";
10
+
11
+ /** The tokens of a call. */
12
+ export interface LlmUsage {
13
+ /** The tokens of the input that the cache did not have. */
14
+ inputTokens: number;
15
+ /** The tokens of the output, with the tokens of the thinking. */
16
+ outputTokens: number;
17
+ /** The tokens of the input that came from the cache. */
18
+ cacheReadInputTokens: number;
19
+ /** The tokens of the input that went into the cache. */
20
+ cacheCreationInputTokens: number;
21
+ }
22
+
23
+ /** The fields of a request that each procedure takes. */
24
+ export interface LlmRequestInput {
25
+ /** The text of the user. */
26
+ prompt: string;
27
+ /** The system text: the instructions to the model. */
28
+ system?: string | undefined;
29
+ /** The model. The default is the model of the module (`claude-sonnet-5-5`). */
30
+ model?: string | undefined;
31
+ /** The most tokens of the output, with the thinking. The default is 16000. */
32
+ maxTokens?: number | undefined;
33
+ /** The temperature, from 0 to 1. Some models accept only the default, and the API refuses another value. */
34
+ temperature?: number | undefined;
35
+ /** The effort of the model. Without it, the model uses its default. */
36
+ effort?: LlmEffort | undefined;
37
+ /** The time limit of the call, in milliseconds. The default is 600000 (10 minutes). */
38
+ timeoutMs?: number | undefined;
39
+ /**
40
+ * True: when a safety classifier declines the request, the API runs it again on the fallback
41
+ * model that it recommends. The default is the option of the module (true).
42
+ */
43
+ fallback?: boolean | undefined;
44
+ }
45
+
46
+ // =============================================================================
47
+ // llm.complete
48
+ // =============================================================================
49
+
50
+ export type LlmCompleteInput = LlmRequestInput;
51
+
52
+ export interface LlmCompleteOutput {
53
+ /** The text of the answer: the text blocks, joined. */
54
+ text: string;
55
+ /** The model that answered. After a fallback, it is the fallback model. */
56
+ model: string;
57
+ /** Why the model stopped: `end_turn`, `max_tokens`, `stop_sequence` or another reason of the API. */
58
+ stopReason: string;
59
+ usage: LlmUsage;
60
+ }
61
+
62
+ // =============================================================================
63
+ // llm.structured
64
+ // =============================================================================
65
+
66
+ export interface LlmStructuredInput extends LlmRequestInput {
67
+ /** The JSON Schema of the value. */
68
+ schema: Record<string, unknown>;
69
+ /**
70
+ * True (default): the API constrains the output to the schema (structured outputs). The
71
+ * procedure removes the keywords that the API does not support, and examines them after the
72
+ * answer. False: the system text gives the schema, and the procedure examines the answer.
73
+ */
74
+ constrained?: boolean | undefined;
75
+ /** The number of new tries after an answer that is not valid: 0 to 2. The default is 2. */
76
+ retries?: number | undefined;
77
+ }
78
+
79
+ export interface LlmStructuredOutput {
80
+ /** The value: the schema accepts it. */
81
+ value: unknown;
82
+ /** The model that gave the value. */
83
+ model: string;
84
+ /** The number of calls to the model: 1 to 3. */
85
+ attempts: number;
86
+ /** The tokens of all the calls. */
87
+ usage: LlmUsage;
88
+ /** The keywords of the schema that the check does not examine, or examines only in part. */
89
+ unchecked: string[];
90
+ }