@nordlys-labs/nordlys-ai-provider 0.1.0 → 0.2.1

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
@@ -33,6 +33,15 @@ const { text } = await generateText({
33
33
  model: nordlys('nordlys/hypernova'),
34
34
  prompt: 'Explain quantum computing',
35
35
  });
36
+
37
+ // With model-level settings
38
+ const { text: creativeText } = await generateText({
39
+ model: nordlys('nordlys/hypernova', {
40
+ temperature: 0.9,
41
+ maxOutputTokens: 2000,
42
+ }),
43
+ prompt: 'Write a creative story',
44
+ });
36
45
  ```
37
46
 
38
47
  ## V3 Content Types
@@ -49,7 +58,7 @@ content.forEach((item) => {
49
58
  case 'text': console.log(item.text); break;
50
59
  case 'reasoning': console.log(item.text); break;
51
60
  case 'file': console.log(item.media_type, item.data); break;
52
- case 'tool-call': console.log(item.toolName, item.args); break;
61
+ case 'tool-call': console.log(item.toolName, item.input); break;
53
62
  }
54
63
  });
55
64
 
@@ -98,16 +107,91 @@ const { text } = await generateText({
98
107
 
99
108
  ## Configuration
100
109
 
110
+ ### Provider Configuration
111
+
101
112
  ```ts
102
113
  import { createNordlys } from '@nordlys-labs/nordlys-ai-provider';
103
114
 
104
- const nordlys = createNordlys({
115
+ // Default provider (uses NORDLYS_API_KEY env var)
116
+ const nordlys = createNordlys();
117
+
118
+ // Provider with explicit API key
119
+ const nordlysWithKey = createNordlys({
120
+ apiKey: 'your-api-key-here',
121
+ });
122
+
123
+ // Provider with custom base URL and API key
124
+ const customNordlys = createNordlys({
105
125
  baseURL: 'https://your-api.com/v1',
106
- apiKey: 'your-key', // or NORDLYS_API_KEY env var
126
+ apiKey: 'your-api-key-here',
107
127
  headers: { 'Custom-Header': 'value' },
108
128
  });
129
+
130
+ // Multiple providers with different API keys
131
+ const provider1 = createNordlys({ apiKey: 'key-for-user-1' });
132
+ const provider2 = createNordlys({ apiKey: 'key-for-user-2' });
133
+
134
+ // Use different providers for different models
135
+ const { text } = await generateText({
136
+ model: provider1('nordlys/hypernova'),
137
+ prompt: 'Hello',
138
+ });
109
139
  ```
110
140
 
141
+ **API Key Priority:**
142
+ 1. Explicit `apiKey` parameter in `createNordlys()`
143
+ 2. `NORDLYS_API_KEY` environment variable (if `apiKey` not provided)
144
+
145
+ ### Model Settings
146
+
147
+ You can pass settings when creating a model instance. These settings will be used as defaults for all calls, but can be overridden at call time:
148
+
149
+ ```ts
150
+ import { nordlys } from '@nordlys-labs/nordlys-ai-provider';
151
+ import { generateText } from 'ai';
152
+
153
+ // Create a model with default settings
154
+ const creativeModel = nordlys('nordlys/hypernova', {
155
+ temperature: 0.9,
156
+ topP: 0.95,
157
+ maxOutputTokens: 2000,
158
+ });
159
+
160
+ // Use the model (settings apply automatically)
161
+ const { text } = await generateText({
162
+ model: creativeModel,
163
+ prompt: 'Write a story',
164
+ });
165
+
166
+ // Override settings at call time (call-level takes precedence)
167
+ const { text: preciseText } = await generateText({
168
+ model: creativeModel,
169
+ prompt: 'Solve this math problem',
170
+ temperature: 0.1, // Overrides model-level temperature
171
+ });
172
+
173
+ // Settings can also be passed to languageModel and chat methods
174
+ const model1 = nordlys.languageModel('nordlys/hypernova', {
175
+ temperature: 0.7,
176
+ });
177
+
178
+ const model2 = nordlys.chat('nordlys/hypernova', {
179
+ temperature: 0.8,
180
+ maxOutputTokens: 1000,
181
+ });
182
+ ```
183
+
184
+ ### Available Settings
185
+
186
+ - `temperature?: number` - Sampling temperature
187
+ - `maxOutputTokens?: number` - Maximum tokens to generate
188
+ - `topP?: number` - Top-p sampling parameter
189
+ - `topK?: number` - Top-k sampling parameter (unsupported, will warn)
190
+ - `frequencyPenalty?: number` - Frequency penalty
191
+ - `presencePenalty?: number` - Presence penalty
192
+ - `stopSequences?: string[]` - Stop sequences
193
+ - `providerOptions?: NordlysProviderOptions` - Provider-specific options
194
+
111
195
  ## Multimodal
112
196
 
113
197
  ```ts
@@ -129,6 +213,7 @@ const { text } = await generateText({
129
213
  - Streaming with all event types
130
214
  - Multimodal inputs (images, audio, PDFs)
131
215
  - Enhanced usage tracking
216
+ - Model-level settings support
132
217
  - AI SDK standard error handling
133
218
  - Full TypeScript support
134
219
 
@@ -151,6 +236,20 @@ try {
151
236
 
152
237
  ## Environment
153
238
 
239
+ The API key can be provided via environment variable or explicitly when creating the provider:
240
+
154
241
  ```bash
242
+ # Set environment variable (used as fallback if apiKey not provided)
155
243
  export NORDLYS_API_KEY="your-api-key"
156
244
  ```
245
+
246
+ ```ts
247
+ // Or provide it explicitly when creating the provider
248
+ import { createNordlys } from '@nordlys-labs/nordlys-ai-provider';
249
+
250
+ const nordlys = createNordlys({
251
+ apiKey: process.env.NORDLYS_API_KEY, // Explicit API key takes precedence
252
+ });
253
+ ```
254
+
255
+ **Note:** If you provide an `apiKey` parameter to `createNordlys()`, it will override the `NORDLYS_API_KEY` environment variable for that provider instance.
package/dist/index.cjs CHANGED
@@ -38,17 +38,17 @@ var import_zod2 = require("zod");
38
38
  var import_provider = require("@ai-sdk/provider");
39
39
  var import_provider_utils = require("@ai-sdk/provider-utils");
40
40
  function convertToolOutput(output) {
41
+ var _a;
41
42
  switch (output.type) {
42
43
  case "text":
43
44
  case "error-text":
44
45
  return output.value;
45
46
  case "json":
46
47
  case "error-json":
47
- return JSON.stringify(output.value);
48
48
  case "content":
49
49
  return JSON.stringify(output.value);
50
50
  case "execution-denied":
51
- return "";
51
+ return (_a = output.reason) != null ? _a : "Tool execution denied.";
52
52
  default:
53
53
  return "";
54
54
  }
@@ -57,6 +57,7 @@ function convertToNordlysChatMessages({
57
57
  prompt,
58
58
  systemMessageMode = "system"
59
59
  }) {
60
+ var _a;
60
61
  const messages = [];
61
62
  const warnings = [];
62
63
  for (const { role, content } of prompt) {
@@ -95,19 +96,23 @@ function convertToNordlysChatMessages({
95
96
  messages.push({
96
97
  role: "user",
97
98
  content: content.map((part, index) => {
98
- var _a, _b;
99
+ var _a2, _b;
99
100
  switch (part.type) {
100
101
  case "text": {
101
102
  return { type: "text", text: part.text };
102
103
  }
103
104
  case "file": {
104
- if ((_a = part.mediaType) == null ? void 0 : _a.startsWith("image/")) {
105
+ if (part.data === void 0 || part.data === null) {
106
+ throw new Error(
107
+ "File part data is required but was undefined or null"
108
+ );
109
+ }
110
+ if ((_a2 = part.mediaType) == null ? void 0 : _a2.startsWith("image/")) {
105
111
  const mediaType = part.mediaType === "image/*" ? "image/jpeg" : part.mediaType;
112
+ const url = part.data instanceof URL ? part.data.toString() : `data:${mediaType};base64,${(0, import_provider_utils.convertToBase64)(part.data)}`;
106
113
  return {
107
114
  type: "image_url",
108
- image_url: {
109
- url: part.data instanceof URL ? part.data.toString() : `data:${mediaType};base64,${(0, import_provider_utils.convertToBase64)(part.data)}`
110
- }
115
+ image_url: { url }
111
116
  };
112
117
  }
113
118
  if (part.mediaType && (part.mediaType === "audio/wav" || part.mediaType === "audio/mp3" || part.mediaType === "audio/mpeg")) {
@@ -116,10 +121,11 @@ function convertToNordlysChatMessages({
116
121
  functionality: "audio file parts with URLs"
117
122
  });
118
123
  }
124
+ const data = typeof part.data === "string" ? part.data : (0, import_provider_utils.convertToBase64)(part.data);
119
125
  return {
120
126
  type: "input_audio",
121
127
  input_audio: {
122
- data: (0, import_provider_utils.convertToBase64)(part.data),
128
+ data,
123
129
  format: part.mediaType === "audio/wav" ? "wav" : "mp3"
124
130
  }
125
131
  };
@@ -130,11 +136,12 @@ function convertToNordlysChatMessages({
130
136
  functionality: "PDF file parts with URLs"
131
137
  });
132
138
  }
139
+ const base64Data = typeof part.data === "string" ? part.data : (0, import_provider_utils.convertToBase64)(part.data);
133
140
  return {
134
141
  type: "file",
135
142
  file: {
136
143
  filename: (_b = part.filename) != null ? _b : `part-${index}.pdf`,
137
- file_data: `data:application/pdf;base64,${(0, import_provider_utils.convertToBase64)(part.data)}`
144
+ file_data: `data:application/pdf;base64,${base64Data}`
138
145
  }
139
146
  };
140
147
  }
@@ -151,14 +158,14 @@ function convertToNordlysChatMessages({
151
158
  break;
152
159
  }
153
160
  case "assistant": {
154
- const textParts = [];
161
+ let text = "";
155
162
  const reasoningParts = [];
156
163
  const generatedFiles = [];
157
164
  const toolCalls = [];
158
165
  for (const part of content) {
159
166
  switch (part.type) {
160
167
  case "text": {
161
- textParts.push(part.text);
168
+ text += part.text;
162
169
  break;
163
170
  }
164
171
  case "reasoning": {
@@ -172,7 +179,7 @@ function convertToNordlysChatMessages({
172
179
  );
173
180
  })() : Buffer.from(part.data).toString("base64");
174
181
  generatedFiles.push({
175
- media_type: part.mediaType,
182
+ media_type: (_a = part.mediaType) != null ? _a : "application/octet-stream",
176
183
  data: dataString
177
184
  });
178
185
  break;
@@ -190,7 +197,6 @@ function convertToNordlysChatMessages({
190
197
  }
191
198
  }
192
199
  }
193
- const text = textParts.join("");
194
200
  const reasoning = reasoningParts.join("");
195
201
  const message = {
196
202
  role: "assistant",
@@ -204,15 +210,16 @@ function convertToNordlysChatMessages({
204
210
  }
205
211
  case "tool": {
206
212
  for (const toolResponse of content) {
207
- if (toolResponse.type === "tool-result") {
208
- const contentValue = convertToolOutput(toolResponse.output);
209
- if (contentValue) {
210
- messages.push({
211
- role: "tool",
212
- tool_call_id: toolResponse.toolCallId,
213
- content: contentValue
214
- });
215
- }
213
+ if (toolResponse.type === "tool-approval-response") {
214
+ continue;
215
+ }
216
+ const contentValue = convertToolOutput(toolResponse.output);
217
+ if (contentValue) {
218
+ messages.push({
219
+ role: "tool",
220
+ tool_call_id: toolResponse.toolCallId,
221
+ content: contentValue
222
+ });
216
223
  }
217
224
  }
218
225
  break;
@@ -569,13 +576,14 @@ var nordlysChatChunkSchema = import_zod2.z.union([
569
576
  })
570
577
  ]);
571
578
  var NordlysChatLanguageModel = class {
572
- constructor(modelId, config) {
579
+ constructor(modelId, settings, config) {
573
580
  this.specificationVersion = "v3";
574
581
  this.supportedUrls = {
575
582
  "application/pdf": [/^https:\/\/.*$/]
576
583
  };
577
584
  this.modelId = modelId;
578
585
  this.config = config;
586
+ this.settings = settings;
579
587
  }
580
588
  get provider() {
581
589
  return this.config.provider;
@@ -594,14 +602,28 @@ var NordlysChatLanguageModel = class {
594
602
  tools,
595
603
  toolChoice
596
604
  }) {
605
+ var _a, _b, _c, _d, _e, _f, _g, _h;
597
606
  const warnings = [];
598
- if (topK != null) {
607
+ const mergedMaxOutputTokens = maxOutputTokens != null ? maxOutputTokens : (_a = this.settings) == null ? void 0 : _a.maxOutputTokens;
608
+ const mergedTemperature = temperature != null ? temperature : (_b = this.settings) == null ? void 0 : _b.temperature;
609
+ const mergedTopP = topP != null ? topP : (_c = this.settings) == null ? void 0 : _c.topP;
610
+ const mergedTopK = topK != null ? topK : (_d = this.settings) == null ? void 0 : _d.topK;
611
+ const mergedFrequencyPenalty = frequencyPenalty != null ? frequencyPenalty : (_e = this.settings) == null ? void 0 : _e.frequencyPenalty;
612
+ const mergedPresencePenalty = presencePenalty != null ? presencePenalty : (_f = this.settings) == null ? void 0 : _f.presencePenalty;
613
+ const mergedStopSequences = stopSequences != null ? stopSequences : (_g = this.settings) == null ? void 0 : _g.stopSequences;
614
+ const mergedProviderOptions = {
615
+ ...(_h = this.settings) == null ? void 0 : _h.providerOptions,
616
+ ...providerOptions
617
+ };
618
+ if (mergedTopK != null) {
599
619
  warnings.push({ type: "unsupported", feature: "topK" });
600
620
  }
601
621
  if (responseFormat != null) {
602
622
  warnings.push({ type: "unsupported", feature: "responseFormat" });
603
623
  }
604
- const result = nordlysProviderOptions.safeParse(providerOptions != null ? providerOptions : {});
624
+ const result = nordlysProviderOptions.safeParse(
625
+ mergedProviderOptions != null ? mergedProviderOptions : {}
626
+ );
605
627
  const nordlysOptions = result.success ? result.data : {};
606
628
  const {
607
629
  tools: nordlysTools,
@@ -617,13 +639,13 @@ var NordlysChatLanguageModel = class {
617
639
  const standardizedArgs = {
618
640
  messages,
619
641
  model: this.modelId,
620
- max_tokens: typeof maxOutputTokens === "number" ? maxOutputTokens : void 0,
642
+ max_tokens: typeof mergedMaxOutputTokens === "number" ? mergedMaxOutputTokens : void 0,
621
643
  max_completion_tokens: nordlysOptions.max_completion_tokens,
622
- temperature,
623
- top_p: topP,
624
- stop: stopSequences,
625
- presence_penalty: presencePenalty,
626
- frequency_penalty: frequencyPenalty,
644
+ temperature: mergedTemperature,
645
+ top_p: mergedTopP,
646
+ stop: mergedStopSequences,
647
+ presence_penalty: mergedPresencePenalty,
648
+ frequency_penalty: mergedFrequencyPenalty,
627
649
  user: nordlysOptions.user,
628
650
  tools: nordlysTools,
629
651
  tool_choice: nordlysToolChoice
@@ -1011,7 +1033,7 @@ var NordlysChatLanguageModel = class {
1011
1033
  // src/nordlys-provider.ts
1012
1034
  function createNordlys(options = {}) {
1013
1035
  var _a;
1014
- const baseURL = (_a = (0, import_provider_utils4.withoutTrailingSlash)(options.baseURL)) != null ? _a : "https://backend.mangoplant-a7a21605.swedencentral.azurecontainerapps.io/v1";
1036
+ const baseURL = (_a = (0, import_provider_utils4.withoutTrailingSlash)(options.baseURL)) != null ? _a : "https://api.nordlyslabs.com/v1";
1015
1037
  const getHeaders = () => ({
1016
1038
  Authorization: `Bearer ${(0, import_provider_utils4.loadApiKey)({
1017
1039
  apiKey: options.apiKey,
@@ -1021,19 +1043,19 @@ function createNordlys(options = {}) {
1021
1043
  "Content-Type": "application/json",
1022
1044
  ...options.headers
1023
1045
  });
1024
- const createChatModel = (modelId) => new NordlysChatLanguageModel(modelId, {
1046
+ const createChatModel = (modelId, settings) => new NordlysChatLanguageModel(modelId, settings, {
1025
1047
  provider: "nordlys.chat",
1026
1048
  baseURL,
1027
1049
  headers: getHeaders,
1028
1050
  fetch: options.fetch
1029
1051
  });
1030
- const provider = function(modelId) {
1052
+ const provider = function(modelId, settings) {
1031
1053
  if (new.target) {
1032
1054
  throw new Error(
1033
1055
  "The Nordlys model function cannot be called with the new keyword."
1034
1056
  );
1035
1057
  }
1036
- return createChatModel(modelId);
1058
+ return createChatModel(modelId, settings);
1037
1059
  };
1038
1060
  provider.languageModel = createChatModel;
1039
1061
  provider.chat = createChatModel;