@particle-academy/prism 0.0.0 → 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 (3) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +155 -155
  3. package/package.json +2 -2
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2024 TJ MIller
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2024 TJ MIller
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,155 +1,155 @@
1
- # @particle-academy/prism
2
-
3
- A TypeScript port of [Prism](https://github.com/Particle-Academy/prism), the
4
- PHP/Laravel unified API layer over LLM providers.
5
-
6
- Real TypeScript: promises, discriminated unions, strict null checking. What it
7
- is *not* is a transliteration — the idioms are this language's. What crosses the
8
- wire, and what comes back, is identical.
9
-
10
- > **Working on this package?** Read **[`AGENTS.md`](AGENTS.md)** first — the boundary
11
- > this package has to hold, the gates that must be green, and the traps that have
12
- > already caught someone.
13
- > `@link AGENTS.md`
14
-
15
- ## Scope
16
-
17
- **Every capability the reference has**, and three of its eighteen providers.
18
- That is the shape of the remaining drift: one axis, not two.
19
-
20
- - **Capabilities — all of them.** `text`, `structured`, `stream`, `embeddings`,
21
- `images`, `moderation`, `audio` (`textToSpeech` and `speechToText`), `files`,
22
- `batch`, `fim`. A user message carries images and documents, and each provider
23
- spells them its own way.
24
- - **Providers — `anthropic`, `mistral`, `openai`.** The OpenAI provider speaks
25
- the **Responses API**; Mistral brings the chat-completions shape, which is a
26
- different wire format rather than a variant of it.
27
-
28
- Not included, and not stubbed: **the tool-execution loop** — a response that
29
- finishes on tool calls is refused with `tool_loop_not_supported` rather than
30
- half-executed — and the fifteen providers the reference has and this port does
31
- not. Asking for one fails with the `unsupported_provider_action` code rather
32
- than returning something plausible.
33
-
34
- Not every capability reaches every provider, because not every provider has one.
35
- `images`, `moderation`, `textToSpeech`, `files` and `batch` are OpenAI-only
36
- here, and `fim` is Mistral-only because Mistral is the only provider that
37
- implements it at all — in the reference as well. See the envelope's port gaps
38
- register for which of those are work someone could do and which are facts about
39
- the provider.
40
-
41
- Every value object serialises **and rebuilds** — `toObject()` and a static
42
- `fromObject()`. The PHP package has only the first half, which left each consumer
43
- to invent the second; one of those inventions shipped a defect that corrupted
44
- stored assistant messages. Both halves ship here, and the rebuild direction is
45
- pinned by fixtures.
46
-
47
- Failures carry a stable `code` (`PrismError.code`). The prose is not part of the
48
- contract and will change; the code will not.
49
-
50
- ## Install
51
-
52
- ```sh
53
- npm install @particle-academy/prism
54
- ```
55
-
56
- Node 20 or newer.
57
-
58
- ## Usage
59
-
60
- ```ts
61
- import { Prism } from '@particle-academy/prism';
62
-
63
- const response = await Prism.text()
64
- .using('openai', 'gpt-4o')
65
- .withSystemPrompt('You are terse.')
66
- .withPrompt('Who are you?')
67
- .asText();
68
-
69
- console.log(response.text);
70
- console.log(response.usage.promptTokens, response.usage.completionTokens);
71
- ```
72
-
73
- Configuration is explicit first, environment second — `OPENAI_API_KEY`,
74
- `OPENAI_URL`, `OPENAI_ORGANIZATION`, `OPENAI_PROJECT`:
75
-
76
- ```ts
77
- Prism.text().using('openai', 'gpt-4o', { apiKey: 'sk-…', transport });
78
- ```
79
-
80
- `transport` is the seam between the provider and the network. Pass one and no
81
- test ever touches a socket:
82
-
83
- ```ts
84
- import type { HttpTransport } from '@particle-academy/prism';
85
-
86
- const transport: HttpTransport = async (request) => ({
87
- status: 200,
88
- headers: {},
89
- body: recordedPayload,
90
- rawBody: JSON.stringify(recordedPayload),
91
- });
92
- ```
93
-
94
- Build a request without sending it, or parse a stored payload without HTTP:
95
-
96
- ```ts
97
- import { buildRequestBody, canonicalJson, parseTextResponse } from '@particle-academy/prism';
98
-
99
- const request = Prism.text().using('openai', 'gpt-4o').withPrompt('hi').toRequest();
100
-
101
- canonicalJson(buildRequestBody(request)); // exactly the bytes that would be POSTed
102
- parseTextResponse(request, recordedPayload); // the same parse a live call performs
103
- ```
104
-
105
- ## Parity
106
-
107
- This port's behaviour is not defined by its own tests. It is pinned by
108
- [**prism-parity**](https://github.com/Particle-Academy/prism-parity) — a
109
- language-independent conformance corpus generated by executing the PHP
110
- reference, which states the rules once so three implementations cannot each
111
- drift into their own reading of them.
112
-
113
- The corpus and its loader install as `@particle-academy/prism-conformance`. The
114
- runner lives in [`conformance/runner.mjs`](conformance/runner.mjs) and implements
115
- prism-parity's subprocess contract, so `scripts/cross-check.mjs` can run this
116
- port and the others side by side and require **identical verdicts** — a stronger
117
- claim than three suites that each went green alone.
118
-
119
- ```sh
120
- git clone https://github.com/Particle-Academy/prism-parity .parity
121
- npm install ./.parity/loaders/ts
122
- npm run conformance # every suite
123
- node conformance/runner.mjs --suite openai-text-request
124
- node conformance/runner.mjs --probe omit-null-keys
125
- ```
126
-
127
- Two rows diverge from the reference on purpose, and the corpus says which and
128
- why: `trq-0021` and `trq-0022`, where the reference drops a prompt of `""` or
129
- `"0"` and this port sends it. Read the corpus for the rest — restating its rules
130
- here would only give them somewhere to drift.
131
-
132
- ### Probes
133
-
134
- `--probe` runs the port with one deliberate defect injected, from the corpus's
135
- `probes/probes.json`. Each probe declares the exact set of case ids it must fail,
136
- and [`tests/probes.test.ts`](tests/probes.test.ts) asserts the observed set
137
- *equals* it — not that it is non-empty. A table every plausible implementation
138
- passes measures nothing.
139
-
140
- The mutants live in [`conformance/mutations.mjs`](conformance/mutations.mjs),
141
- outside `src/`: a defect you can switch on in production is a defect.
142
-
143
- ## Development
144
-
145
- ```sh
146
- npm run build # tsc → dist/
147
- npm run typecheck
148
- npm run lint
149
- npm test
150
- npm run conformance
151
- ```
152
-
153
- ## License
154
-
155
- MIT. See [LICENSE](LICENSE).
1
+ # @particle-academy/prism
2
+
3
+ A TypeScript port of [Prism](https://github.com/Particle-Academy/prism), the
4
+ PHP/Laravel unified API layer over LLM providers.
5
+
6
+ Real TypeScript: promises, discriminated unions, strict null checking. What it
7
+ is *not* is a transliteration — the idioms are this language's. What crosses the
8
+ wire, and what comes back, is identical.
9
+
10
+ > **Working on this package?** Read **[`AGENTS.md`](AGENTS.md)** first — the boundary
11
+ > this package has to hold, the gates that must be green, and the traps that have
12
+ > already caught someone.
13
+ > `@link AGENTS.md`
14
+
15
+ ## Scope
16
+
17
+ **Every capability the reference has**, and three of its eighteen providers.
18
+ That is the shape of the remaining drift: one axis, not two.
19
+
20
+ - **Capabilities — all of them.** `text`, `structured`, `stream`, `embeddings`,
21
+ `images`, `moderation`, `audio` (`textToSpeech` and `speechToText`), `files`,
22
+ `batch`, `fim`. A user message carries images and documents, and each provider
23
+ spells them its own way.
24
+ - **Providers — `anthropic`, `mistral`, `openai`.** The OpenAI provider speaks
25
+ the **Responses API**; Mistral brings the chat-completions shape, which is a
26
+ different wire format rather than a variant of it.
27
+
28
+ Not included, and not stubbed: **the tool-execution loop** — a response that
29
+ finishes on tool calls is refused with `tool_loop_not_supported` rather than
30
+ half-executed — and the fifteen providers the reference has and this port does
31
+ not. Asking for one fails with the `unsupported_provider_action` code rather
32
+ than returning something plausible.
33
+
34
+ Not every capability reaches every provider, because not every provider has one.
35
+ `images`, `moderation`, `textToSpeech`, `files` and `batch` are OpenAI-only
36
+ here, and `fim` is Mistral-only because Mistral is the only provider that
37
+ implements it at all — in the reference as well. See the envelope's port gaps
38
+ register for which of those are work someone could do and which are facts about
39
+ the provider.
40
+
41
+ Every value object serialises **and rebuilds** — `toObject()` and a static
42
+ `fromObject()`. The PHP package has only the first half, which left each consumer
43
+ to invent the second; one of those inventions shipped a defect that corrupted
44
+ stored assistant messages. Both halves ship here, and the rebuild direction is
45
+ pinned by fixtures.
46
+
47
+ Failures carry a stable `code` (`PrismError.code`). The prose is not part of the
48
+ contract and will change; the code will not.
49
+
50
+ ## Install
51
+
52
+ ```sh
53
+ npm install @particle-academy/prism
54
+ ```
55
+
56
+ Node 20 or newer.
57
+
58
+ ## Usage
59
+
60
+ ```ts
61
+ import { Prism } from '@particle-academy/prism';
62
+
63
+ const response = await Prism.text()
64
+ .using('openai', 'gpt-4o')
65
+ .withSystemPrompt('You are terse.')
66
+ .withPrompt('Who are you?')
67
+ .asText();
68
+
69
+ console.log(response.text);
70
+ console.log(response.usage.promptTokens, response.usage.completionTokens);
71
+ ```
72
+
73
+ Configuration is explicit first, environment second — `OPENAI_API_KEY`,
74
+ `OPENAI_URL`, `OPENAI_ORGANIZATION`, `OPENAI_PROJECT`:
75
+
76
+ ```ts
77
+ Prism.text().using('openai', 'gpt-4o', { apiKey: 'sk-…', transport });
78
+ ```
79
+
80
+ `transport` is the seam between the provider and the network. Pass one and no
81
+ test ever touches a socket:
82
+
83
+ ```ts
84
+ import type { HttpTransport } from '@particle-academy/prism';
85
+
86
+ const transport: HttpTransport = async (request) => ({
87
+ status: 200,
88
+ headers: {},
89
+ body: recordedPayload,
90
+ rawBody: JSON.stringify(recordedPayload),
91
+ });
92
+ ```
93
+
94
+ Build a request without sending it, or parse a stored payload without HTTP:
95
+
96
+ ```ts
97
+ import { buildRequestBody, canonicalJson, parseTextResponse } from '@particle-academy/prism';
98
+
99
+ const request = Prism.text().using('openai', 'gpt-4o').withPrompt('hi').toRequest();
100
+
101
+ canonicalJson(buildRequestBody(request)); // exactly the bytes that would be POSTed
102
+ parseTextResponse(request, recordedPayload); // the same parse a live call performs
103
+ ```
104
+
105
+ ## Parity
106
+
107
+ This port's behaviour is not defined by its own tests. It is pinned by
108
+ [**prism-parity**](https://github.com/Particle-Academy/prism-parity) — a
109
+ language-independent conformance corpus generated by executing the PHP
110
+ reference, which states the rules once so three implementations cannot each
111
+ drift into their own reading of them.
112
+
113
+ The corpus and its loader install as `@particle-academy/prism-conformance`. The
114
+ runner lives in [`conformance/runner.mjs`](conformance/runner.mjs) and implements
115
+ prism-parity's subprocess contract, so `scripts/cross-check.mjs` can run this
116
+ port and the others side by side and require **identical verdicts** — a stronger
117
+ claim than three suites that each went green alone.
118
+
119
+ ```sh
120
+ git clone https://github.com/Particle-Academy/prism-parity .parity
121
+ npm install ./.parity/loaders/ts
122
+ npm run conformance # every suite
123
+ node conformance/runner.mjs --suite openai-text-request
124
+ node conformance/runner.mjs --probe omit-null-keys
125
+ ```
126
+
127
+ Two rows diverge from the reference on purpose, and the corpus says which and
128
+ why: `trq-0021` and `trq-0022`, where the reference drops a prompt of `""` or
129
+ `"0"` and this port sends it. Read the corpus for the rest — restating its rules
130
+ here would only give them somewhere to drift.
131
+
132
+ ### Probes
133
+
134
+ `--probe` runs the port with one deliberate defect injected, from the corpus's
135
+ `probes/probes.json`. Each probe declares the exact set of case ids it must fail,
136
+ and [`tests/probes.test.ts`](tests/probes.test.ts) asserts the observed set
137
+ *equals* it — not that it is non-empty. A table every plausible implementation
138
+ passes measures nothing.
139
+
140
+ The mutants live in [`conformance/mutations.mjs`](conformance/mutations.mjs),
141
+ outside `src/`: a defect you can switch on in production is a defect.
142
+
143
+ ## Development
144
+
145
+ ```sh
146
+ npm run build # tsc → dist/
147
+ npm run typecheck
148
+ npm run lint
149
+ npm test
150
+ npm run conformance
151
+ ```
152
+
153
+ ## License
154
+
155
+ MIT. See [LICENSE](LICENSE).
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@particle-academy/prism",
3
- "version": "0.0.0",
4
- "description": "TypeScript port of Prism — a unified API layer over LLM providers.",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript port of Prism \u2014 a unified API layer over LLM providers.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",