@nightowlsdev/model-openai 0.1.0 → 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 +101 -0
- package/dist/index.cjs +26 -2
- package/dist/index.d.cts +24 -1
- package/dist/index.d.ts +24 -1
- package/dist/index.js +23 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# @nightowlsdev/model-openai
|
|
2
|
+
|
|
3
|
+
> Swappable `modelFactory` provider plugin — wire OpenAI models into a Night Owls swarm with one line; `owl install model-openai` does the rest.
|
|
4
|
+
|
|
5
|
+
This package is a thin adapter between the Night Owls core and OpenAI's API. It exposes a `modelFactory` factory (`openaiModels()`) that maps any OpenAI model id to an AI SDK `LanguageModelV3`, and a declarative `nightOwlsPlugin` manifest that the `@nightowlsdev/cli` uses for scaffolding, `owl install`, and `owl model-openai info`.
|
|
6
|
+
|
|
7
|
+
Model provider plugins are **mutually exclusive** — install exactly one `@nightowlsdev/model-*` package per swarm. They carry zero `@nightowlsdev/core` or `@mastra/*` dependencies and are safe to tree-shake.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
pnpm add @nightowlsdev/model-openai
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
No required peer dependencies. `@ai-sdk/openai` is a direct dependency and is included.
|
|
16
|
+
|
|
17
|
+
If you are using the CLI, the recommended installation path is:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
owl install model-openai
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This runs the plugin's `init` handler, sets up the config marker, and prompts for `OPENAI_API_KEY` + `MODEL_ID`.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { openaiModels } from "@nightowlsdev/model-openai";
|
|
29
|
+
import { defineSwarm } from "@nightowlsdev/core";
|
|
30
|
+
|
|
31
|
+
const swarm = defineSwarm({
|
|
32
|
+
modelFactory: openaiModels(), // reads OPENAI_API_KEY from env
|
|
33
|
+
models: {
|
|
34
|
+
allow: [process.env.MODEL_ID ?? "gpt-4o"],
|
|
35
|
+
},
|
|
36
|
+
agents: [/* … */],
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Pass an explicit key instead of relying on the environment:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
const swarm = defineSwarm({
|
|
44
|
+
modelFactory: openaiModels({ apiKey: "sk-…" }),
|
|
45
|
+
// …
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API
|
|
50
|
+
|
|
51
|
+
### `openaiModels(opts?) → (modelId: string) => LanguageModelV3`
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
function openaiModels(opts?: { apiKey?: string }): (modelId: string) => unknown;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Returns a `modelFactory` function compatible with `defineSwarm`. Internally calls `createOpenAI` from `@ai-sdk/openai`.
|
|
58
|
+
|
|
59
|
+
- `opts.apiKey` — optional override; when omitted, `@ai-sdk/openai` reads `OPENAI_API_KEY` from the environment.
|
|
60
|
+
- The returned factory accepts any OpenAI model id string (e.g. `"gpt-4o"`, `"gpt-4o-mini"`, `"o3"`) and returns the corresponding AI SDK model instance.
|
|
61
|
+
|
|
62
|
+
### `nightOwlsPlugin`
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
export const nightOwlsPlugin: {
|
|
66
|
+
name: "model-openai";
|
|
67
|
+
version: string;
|
|
68
|
+
kind: "model";
|
|
69
|
+
pkg: "@nightowlsdev/model-openai";
|
|
70
|
+
description: string;
|
|
71
|
+
env: Array<{ key: string; example: string; comment: string }>;
|
|
72
|
+
config: { import: string; snippet: string; marker: "model" };
|
|
73
|
+
init: (ctx: { cwd: string; log: (m: string) => void }) => void;
|
|
74
|
+
commands: Array<{ name: string; description: string; run: (ctx: Ctx) => void }>;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The declarative plugin manifest consumed by `@nightowlsdev/cli`. It is **data-only** — it does not import `@nightowlsdev/cli` (no dependency cycle). Key fields:
|
|
79
|
+
|
|
80
|
+
| Field | Value |
|
|
81
|
+
|---|---|
|
|
82
|
+
| `kind` | `"model"` |
|
|
83
|
+
| `config.marker` | `"model"` — the `// nightowls:model` marker in `nightowls.config.ts` that `owl install` replaces |
|
|
84
|
+
| `config.snippet` | `modelFactory = openaiModels();` |
|
|
85
|
+
| `env[].key` | `OPENAI_API_KEY`, `MODEL_ID` |
|
|
86
|
+
|
|
87
|
+
## Configuration / Environment
|
|
88
|
+
|
|
89
|
+
| Variable | Required | Example | Purpose |
|
|
90
|
+
|---|---|---|---|
|
|
91
|
+
| `OPENAI_API_KEY` | Yes (unless passed via `opts.apiKey`) | `sk-…` | OpenAI API key; read by `@ai-sdk/openai` at call time |
|
|
92
|
+
| `MODEL_ID` | Recommended | `gpt-4o` | The model id to place in `models.allow` in `nightowls.config.ts` |
|
|
93
|
+
|
|
94
|
+
Set both in `.env.local` (or your environment). `MODEL_ID` is not read by this package at runtime — it is a convention used by the config scaffold and `owl model-openai info` to communicate which model id to use.
|
|
95
|
+
|
|
96
|
+
## Plugin manifest
|
|
97
|
+
|
|
98
|
+
This package exports `nightOwlsPlugin` with `kind: "model"`. The CLI consumes it in two ways:
|
|
99
|
+
|
|
100
|
+
- **`owl install model-openai`** — runs `init`, injects `import { openaiModels } from "@nightowlsdev/model-openai"` at the top of `nightowls.config.ts`, and replaces the `// nightowls:model` marker with `modelFactory = openaiModels();`.
|
|
101
|
+
- **`owl model-openai info`** — runs the `"info"` command, which prints the required env vars (`OPENAI_API_KEY`, `MODEL_ID`) and the config snippet without making any network calls.
|
package/dist/index.cjs
CHANGED
|
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
nightOwlsPlugin: () => nightOwlsPlugin,
|
|
24
|
-
|
|
24
|
+
openaiEmbeddings: () => openaiEmbeddings,
|
|
25
|
+
openaiModels: () => openaiModels,
|
|
26
|
+
pinEmbeddingDimensions: () => pinEmbeddingDimensions
|
|
25
27
|
});
|
|
26
28
|
module.exports = __toCommonJS(index_exports);
|
|
27
29
|
var import_openai = require("@ai-sdk/openai");
|
|
@@ -29,6 +31,26 @@ function openaiModels(opts = {}) {
|
|
|
29
31
|
const provider = (0, import_openai.createOpenAI)(opts.apiKey ? { apiKey: opts.apiKey } : {});
|
|
30
32
|
return (modelId) => provider(modelId);
|
|
31
33
|
}
|
|
34
|
+
function openaiEmbeddings(opts) {
|
|
35
|
+
const provider = (0, import_openai.createOpenAI)(opts.apiKey ? { apiKey: opts.apiKey } : {});
|
|
36
|
+
const model = provider.embedding(opts.model);
|
|
37
|
+
return opts.dimensions === void 0 ? model : pinEmbeddingDimensions(model, "openai", opts.dimensions);
|
|
38
|
+
}
|
|
39
|
+
function pinEmbeddingDimensions(model, providerKey, dimensions) {
|
|
40
|
+
return new Proxy(model, {
|
|
41
|
+
get(target, prop, recv) {
|
|
42
|
+
if (prop !== "doEmbed") return Reflect.get(target, prop, recv);
|
|
43
|
+
const orig = Reflect.get(target, prop, recv);
|
|
44
|
+
return (options) => {
|
|
45
|
+
const po = options?.providerOptions ?? {};
|
|
46
|
+
return orig.call(target, {
|
|
47
|
+
...options,
|
|
48
|
+
providerOptions: { ...po, [providerKey]: { ...po[providerKey] ?? {}, dimensions } }
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
32
54
|
var nightOwlsPlugin = {
|
|
33
55
|
name: "model-openai",
|
|
34
56
|
version: "0.0.0",
|
|
@@ -61,5 +83,7 @@ var nightOwlsPlugin = {
|
|
|
61
83
|
// Annotate the CommonJS export names for ESM import in node:
|
|
62
84
|
0 && (module.exports = {
|
|
63
85
|
nightOwlsPlugin,
|
|
64
|
-
|
|
86
|
+
openaiEmbeddings,
|
|
87
|
+
openaiModels,
|
|
88
|
+
pinEmbeddingDimensions
|
|
65
89
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -9,6 +9,29 @@
|
|
|
9
9
|
declare function openaiModels(opts?: {
|
|
10
10
|
apiKey?: string;
|
|
11
11
|
}): (modelId: string) => unknown;
|
|
12
|
+
/** Options for {@link openaiEmbeddings}. `model` is the embedding model id (e.g. `text-embedding-3-small`);
|
|
13
|
+
* `dimensions` (optional) PINS the output vector size to match the host's pgvector column — when set it is
|
|
14
|
+
* injected as a provider option on every embed call, so it takes effect even through Mastra's memory recall. */
|
|
15
|
+
interface OpenAIEmbeddingsOpts {
|
|
16
|
+
apiKey?: string;
|
|
17
|
+
model: string;
|
|
18
|
+
dimensions?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* FR-011 — an EMBEDDING-model factory parallel to {@link openaiModels}, so a RAG/memory consumer gets its
|
|
22
|
+
* embedder from the SAME provider adapter it already installed (no bespoke `fetch` to the embeddings endpoint).
|
|
23
|
+
* Fill `MemoryConfig.embedder` with the return value, or pass it to the AI SDK's `embed`/`embedMany`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* memory: { embedder: openaiEmbeddings({ model: "text-embedding-3-small", dimensions: 1536 }) }
|
|
27
|
+
*/
|
|
28
|
+
declare function openaiEmbeddings(opts: OpenAIEmbeddingsOpts): unknown;
|
|
29
|
+
/**
|
|
30
|
+
* Wrap an AI-SDK embedding model so every `doEmbed` call carries `providerOptions.<key>.dimensions`, pinning the
|
|
31
|
+
* output vector size without depending on the call site to pass it (the host's caller, or Mastra memory, won't).
|
|
32
|
+
* A `Proxy` preserves the model's getters/prototype (a spread would drop them).
|
|
33
|
+
*/
|
|
34
|
+
declare function pinEmbeddingDimensions(model: unknown, providerKey: string, dimensions: number): unknown;
|
|
12
35
|
/** Structural context for this adapter's pure command handlers — matches the CLI's PluginCommandContext. */
|
|
13
36
|
type Ctx = {
|
|
14
37
|
cwd: string;
|
|
@@ -52,4 +75,4 @@ declare const nightOwlsPlugin: {
|
|
|
52
75
|
}];
|
|
53
76
|
};
|
|
54
77
|
|
|
55
|
-
export { nightOwlsPlugin, openaiModels };
|
|
78
|
+
export { type OpenAIEmbeddingsOpts, nightOwlsPlugin, openaiEmbeddings, openaiModels, pinEmbeddingDimensions };
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,29 @@
|
|
|
9
9
|
declare function openaiModels(opts?: {
|
|
10
10
|
apiKey?: string;
|
|
11
11
|
}): (modelId: string) => unknown;
|
|
12
|
+
/** Options for {@link openaiEmbeddings}. `model` is the embedding model id (e.g. `text-embedding-3-small`);
|
|
13
|
+
* `dimensions` (optional) PINS the output vector size to match the host's pgvector column — when set it is
|
|
14
|
+
* injected as a provider option on every embed call, so it takes effect even through Mastra's memory recall. */
|
|
15
|
+
interface OpenAIEmbeddingsOpts {
|
|
16
|
+
apiKey?: string;
|
|
17
|
+
model: string;
|
|
18
|
+
dimensions?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* FR-011 — an EMBEDDING-model factory parallel to {@link openaiModels}, so a RAG/memory consumer gets its
|
|
22
|
+
* embedder from the SAME provider adapter it already installed (no bespoke `fetch` to the embeddings endpoint).
|
|
23
|
+
* Fill `MemoryConfig.embedder` with the return value, or pass it to the AI SDK's `embed`/`embedMany`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* memory: { embedder: openaiEmbeddings({ model: "text-embedding-3-small", dimensions: 1536 }) }
|
|
27
|
+
*/
|
|
28
|
+
declare function openaiEmbeddings(opts: OpenAIEmbeddingsOpts): unknown;
|
|
29
|
+
/**
|
|
30
|
+
* Wrap an AI-SDK embedding model so every `doEmbed` call carries `providerOptions.<key>.dimensions`, pinning the
|
|
31
|
+
* output vector size without depending on the call site to pass it (the host's caller, or Mastra memory, won't).
|
|
32
|
+
* A `Proxy` preserves the model's getters/prototype (a spread would drop them).
|
|
33
|
+
*/
|
|
34
|
+
declare function pinEmbeddingDimensions(model: unknown, providerKey: string, dimensions: number): unknown;
|
|
12
35
|
/** Structural context for this adapter's pure command handlers — matches the CLI's PluginCommandContext. */
|
|
13
36
|
type Ctx = {
|
|
14
37
|
cwd: string;
|
|
@@ -52,4 +75,4 @@ declare const nightOwlsPlugin: {
|
|
|
52
75
|
}];
|
|
53
76
|
};
|
|
54
77
|
|
|
55
|
-
export { nightOwlsPlugin, openaiModels };
|
|
78
|
+
export { type OpenAIEmbeddingsOpts, nightOwlsPlugin, openaiEmbeddings, openaiModels, pinEmbeddingDimensions };
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,26 @@ function openaiModels(opts = {}) {
|
|
|
4
4
|
const provider = createOpenAI(opts.apiKey ? { apiKey: opts.apiKey } : {});
|
|
5
5
|
return (modelId) => provider(modelId);
|
|
6
6
|
}
|
|
7
|
+
function openaiEmbeddings(opts) {
|
|
8
|
+
const provider = createOpenAI(opts.apiKey ? { apiKey: opts.apiKey } : {});
|
|
9
|
+
const model = provider.embedding(opts.model);
|
|
10
|
+
return opts.dimensions === void 0 ? model : pinEmbeddingDimensions(model, "openai", opts.dimensions);
|
|
11
|
+
}
|
|
12
|
+
function pinEmbeddingDimensions(model, providerKey, dimensions) {
|
|
13
|
+
return new Proxy(model, {
|
|
14
|
+
get(target, prop, recv) {
|
|
15
|
+
if (prop !== "doEmbed") return Reflect.get(target, prop, recv);
|
|
16
|
+
const orig = Reflect.get(target, prop, recv);
|
|
17
|
+
return (options) => {
|
|
18
|
+
const po = options?.providerOptions ?? {};
|
|
19
|
+
return orig.call(target, {
|
|
20
|
+
...options,
|
|
21
|
+
providerOptions: { ...po, [providerKey]: { ...po[providerKey] ?? {}, dimensions } }
|
|
22
|
+
});
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
7
27
|
var nightOwlsPlugin = {
|
|
8
28
|
name: "model-openai",
|
|
9
29
|
version: "0.0.0",
|
|
@@ -35,5 +55,7 @@ var nightOwlsPlugin = {
|
|
|
35
55
|
};
|
|
36
56
|
export {
|
|
37
57
|
nightOwlsPlugin,
|
|
38
|
-
|
|
58
|
+
openaiEmbeddings,
|
|
59
|
+
openaiModels,
|
|
60
|
+
pinEmbeddingDimensions
|
|
39
61
|
};
|