purra-openai 1.0.0 → 1.0.4
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 +4 -0
- package/dist/chat.d.ts +1 -0
- package/dist/chat.js +18 -4
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# purra-openai · TypeScript
|
|
2
2
|
|
|
3
|
+
In 1.0.0 development, Chat Completions accepts the optional
|
|
4
|
+
[static image contract](../../../docs/static-images.md) with `imageInput: true`.
|
|
5
|
+
Responses image input is not implemented in this slice.
|
|
6
|
+
|
|
3
7
|
English | [简体中文](README.zh-CN.md)
|
|
4
8
|
|
|
5
9
|
OpenAI model gateways for PurrA using the official SDK. Supports text, function
|
package/dist/chat.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import OpenAI from "openai";
|
|
|
2
2
|
import type { ChatCompletionCreateParamsNonStreaming } from "openai/resources/chat/completions";
|
|
3
3
|
import type { ModelCapabilitySnapshot, ModelGateway, ModelRequest, ModelStream, ModelTurn } from "purra";
|
|
4
4
|
export interface OpenAIChatCompletionsOptions {
|
|
5
|
+
readonly imageInput?: boolean;
|
|
5
6
|
readonly client?: OpenAI;
|
|
6
7
|
readonly model: string;
|
|
7
8
|
readonly capabilities: ModelCapabilitySnapshot;
|
package/dist/chat.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { validateOutput } from "./structured.js";
|
|
2
2
|
import OpenAI from "openai";
|
|
3
|
-
import { AgentCanceledError, AgentError } from "purra";
|
|
3
|
+
import { AgentCanceledError, AgentError, parseStaticImageContent } from "purra";
|
|
4
4
|
function text(value) {
|
|
5
5
|
if (value === null)
|
|
6
6
|
return "";
|
|
@@ -8,10 +8,18 @@ function text(value) {
|
|
|
8
8
|
throw new TypeError("OpenAI Chat Completions accepts text content only");
|
|
9
9
|
return value;
|
|
10
10
|
}
|
|
11
|
-
function messages(input) {
|
|
11
|
+
function messages(input, imageInput) {
|
|
12
12
|
return input.map(m => {
|
|
13
13
|
if (m.toolCalls?.length && m.role !== "assistant")
|
|
14
14
|
throw new TypeError("Only assistant messages may carry tool calls");
|
|
15
|
+
const images = parseStaticImageContent(m.content);
|
|
16
|
+
if (images !== undefined) {
|
|
17
|
+
if (!imageInput || m.role !== "user")
|
|
18
|
+
throw new TypeError("Static images require user role and host-enabled image input");
|
|
19
|
+
return { role: "user", content: [{ type: "text", text: images.text }, ...images.images.map(image => ({
|
|
20
|
+
type: "image_url", image_url: { url: `data:${image.mediaType};base64,${image.dataBase64}` },
|
|
21
|
+
}))] };
|
|
22
|
+
}
|
|
15
23
|
const content = text(m.content);
|
|
16
24
|
if (m.role === "tool") {
|
|
17
25
|
if (!m.toolCallId)
|
|
@@ -57,6 +65,8 @@ export class OpenAIChatCompletionsGateway {
|
|
|
57
65
|
#client;
|
|
58
66
|
#options;
|
|
59
67
|
constructor(options) {
|
|
68
|
+
if (options.imageInput !== undefined && typeof options.imageInput !== "boolean")
|
|
69
|
+
throw new TypeError("imageInput must be boolean");
|
|
60
70
|
if (!options.model.trim())
|
|
61
71
|
throw new TypeError("Model name is required");
|
|
62
72
|
if (!Number.isFinite(options.timeoutMs ?? 60000) || (options.timeoutMs ?? 60000) <= 0)
|
|
@@ -70,10 +80,14 @@ export class OpenAIChatCompletionsGateway {
|
|
|
70
80
|
}
|
|
71
81
|
#request(request) {
|
|
72
82
|
this.validateOutputContract(request);
|
|
83
|
+
if (request.messages.some(message => parseStaticImageContent(message.content) !== undefined)
|
|
84
|
+
&& (this.capabilities.protocol?.imageInput !== "supported" || request.capabilitySnapshot?.protocol.imageInput !== "supported")) {
|
|
85
|
+
throw new AgentError("model_capability_incompatible", "Model profile does not declare image input support");
|
|
86
|
+
}
|
|
73
87
|
if (!request.outputBudget)
|
|
74
88
|
throw new TypeError("OpenAI gateway requires a resolved generation budget");
|
|
75
89
|
const o = this.#options;
|
|
76
|
-
return { model: o.model, messages: messages(request.messages), store: false, max_completion_tokens: request.outputBudget.maxGenerationTokens,
|
|
90
|
+
return { model: o.model, messages: messages(request.messages, o.imageInput ?? false), store: false, max_completion_tokens: request.outputBudget.maxGenerationTokens,
|
|
77
91
|
...(request.outputContract?.mode === "native_required" ? { response_format: { type: "json_schema", json_schema: {
|
|
78
92
|
name: "purra_output", strict: true, schema: JSON.parse(JSON.stringify(request.outputContract.schema)),
|
|
79
93
|
} } } : {}),
|
|
@@ -140,7 +154,7 @@ export class OpenAIChatCompletionsGateway {
|
|
|
140
154
|
yield { contentDelta: content, toolCallDeltas: calls };
|
|
141
155
|
else
|
|
142
156
|
yield { type: "activity", kind: "transport" };
|
|
143
|
-
if (c.finish_reason
|
|
157
|
+
if (c.finish_reason != null)
|
|
144
158
|
terminal = finish(c.finish_reason);
|
|
145
159
|
}
|
|
146
160
|
if (terminal === undefined)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "purra-openai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Official OpenAI Responses and Chat Completions SDK adapter for PurrA",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"openai": "7.9.0"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"purra": "1.0.
|
|
23
|
+
"purra": "1.0.4"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"purra": "file:../../../typescript",
|