@meetopenbot/openbot 1.0.1 → 1.0.3
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/dist/auto-model.js +7 -11
- package/dist/images.js +29 -11
- package/dist/model.js +11 -26
- package/dist/runtime.js +2 -4
- package/package.json +2 -1
package/dist/auto-model.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shouldUseCreditsAuth } from '@meetopenbot/plugin-sdk';
|
|
1
|
+
import { BYOK_ENV_KEYS, shouldUseCreditsAuth } from '@meetopenbot/plugin-sdk';
|
|
2
2
|
/** Product alias shown in the picker. Never sent to a provider or the credits proxy. */
|
|
3
3
|
export const AUTO_MODEL_ID = 'openbot/auto';
|
|
4
4
|
export const AUTO_MODEL_OPTION = {
|
|
@@ -11,12 +11,9 @@ export const AUTO_MODEL_OPTION = {
|
|
|
11
11
|
* order, failover, intent routing) without changing the `openbot/auto` alias.
|
|
12
12
|
*/
|
|
13
13
|
export const AUTO_MODEL_POOL = ['deepseek/deepseek-v4-flash'];
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
google: 'GOOGLE_GENERATIVE_AI_API_KEY',
|
|
18
|
-
deepseek: 'DEEPSEEK_API_KEY',
|
|
19
|
-
};
|
|
14
|
+
function providerByokKeys(provider) {
|
|
15
|
+
return BYOK_ENV_KEYS[provider];
|
|
16
|
+
}
|
|
20
17
|
export function isAutoModel(modelString) {
|
|
21
18
|
if (!modelString)
|
|
22
19
|
return true;
|
|
@@ -27,8 +24,8 @@ function providerOf(modelString) {
|
|
|
27
24
|
return modelString.split('/')[0] ?? '';
|
|
28
25
|
}
|
|
29
26
|
function providerHasByokKey(provider, env) {
|
|
30
|
-
const
|
|
31
|
-
return Boolean(key
|
|
27
|
+
const keys = providerByokKeys(provider);
|
|
28
|
+
return Boolean(keys?.some((key) => env[key]?.trim()));
|
|
32
29
|
}
|
|
33
30
|
/** Concrete models Auto may call, filtered by credits vs BYOK keys. */
|
|
34
31
|
export function listAutoModelCandidates(ctx) {
|
|
@@ -57,8 +54,7 @@ export function missingByokApiKeyMessage(modelString, ctx) {
|
|
|
57
54
|
const provider = providerOf(expandModelString(modelString, ctx));
|
|
58
55
|
if (providerHasByokKey(provider, env))
|
|
59
56
|
return undefined;
|
|
60
|
-
|
|
61
|
-
if (!envVar)
|
|
57
|
+
if (!providerByokKeys(provider))
|
|
62
58
|
return undefined;
|
|
63
59
|
return `${provider} API key is missing`;
|
|
64
60
|
}
|
package/dist/images.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shouldUseCreditsAuth } from '@meetopenbot/plugin-sdk';
|
|
1
|
+
import { BYOK_ENV_KEYS, shouldUseCreditsAuth } from '@meetopenbot/plugin-sdk';
|
|
2
2
|
export function isImageAttachment(file) {
|
|
3
3
|
if (file.mimeType?.startsWith('image/'))
|
|
4
4
|
return true;
|
|
@@ -13,35 +13,53 @@ export function eventsHaveImages(events) {
|
|
|
13
13
|
return Boolean(event.data?.attachments?.some(isImageAttachment));
|
|
14
14
|
});
|
|
15
15
|
}
|
|
16
|
+
function hasAnyKey(env, keys) {
|
|
17
|
+
return keys.some((key) => Boolean(env[key]?.trim()));
|
|
18
|
+
}
|
|
16
19
|
/** Auto uses DeepSeek, which cannot see pictures. Pick a cheap vision model. */
|
|
17
20
|
export function pickVisionModel(authMode, env = process.env) {
|
|
18
21
|
if (shouldUseCreditsAuth({ authMode }))
|
|
19
22
|
return 'google/gemini-3.5-flash';
|
|
20
|
-
if (env.
|
|
23
|
+
if (hasAnyKey(env, BYOK_ENV_KEYS.openai))
|
|
21
24
|
return 'openai/gpt-4o-mini';
|
|
22
|
-
if (env.
|
|
25
|
+
if (hasAnyKey(env, BYOK_ENV_KEYS.google))
|
|
23
26
|
return 'google/gemini-3.5-flash';
|
|
24
|
-
if (env.
|
|
27
|
+
if (hasAnyKey(env, BYOK_ENV_KEYS.anthropic))
|
|
25
28
|
return 'anthropic/claude-haiku-4-5';
|
|
26
29
|
return undefined;
|
|
27
30
|
}
|
|
31
|
+
function invokeRole(role) {
|
|
32
|
+
return role === 'agent' ? 'user' : role;
|
|
33
|
+
}
|
|
28
34
|
/** Put image files on matching user messages as AI SDK `file` parts. */
|
|
29
35
|
export async function addImageParts(messages, events, read) {
|
|
30
36
|
const result = [...messages];
|
|
31
|
-
let
|
|
37
|
+
let userCursor = 0;
|
|
38
|
+
const takeNextUserMessageIndex = () => {
|
|
39
|
+
while (userCursor < result.length) {
|
|
40
|
+
const index = userCursor;
|
|
41
|
+
userCursor += 1;
|
|
42
|
+
if (result[index]?.role === 'user')
|
|
43
|
+
return index;
|
|
44
|
+
}
|
|
45
|
+
return -1;
|
|
46
|
+
};
|
|
32
47
|
for (const event of events) {
|
|
33
48
|
if (event.meta?.parentToolCallId)
|
|
34
49
|
continue;
|
|
35
50
|
if (event.type !== 'agent:invoke' || !event.data?.role)
|
|
36
51
|
continue;
|
|
37
|
-
const
|
|
52
|
+
const attachments = event.data.attachments ?? [];
|
|
38
53
|
const text = event.data.content ?? '';
|
|
39
|
-
if (!text &&
|
|
54
|
+
if (!text && attachments.length === 0)
|
|
55
|
+
continue;
|
|
56
|
+
if (invokeRole(event.data.role) !== 'user')
|
|
40
57
|
continue;
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
if (
|
|
58
|
+
const index = takeNextUserMessageIndex();
|
|
59
|
+
const files = attachments.filter(isImageAttachment);
|
|
60
|
+
if (index === -1 || files.length === 0)
|
|
44
61
|
continue;
|
|
62
|
+
const current = result[index];
|
|
45
63
|
const parts = [];
|
|
46
64
|
if (typeof current.content === 'string' && current.content) {
|
|
47
65
|
parts.push({ type: 'text', text: current.content });
|
|
@@ -57,7 +75,7 @@ export async function addImageParts(messages, events, read) {
|
|
|
57
75
|
});
|
|
58
76
|
}
|
|
59
77
|
if (parts.some((part) => part.type === 'file')) {
|
|
60
|
-
result[
|
|
78
|
+
result[index] = { role: 'user', content: parts };
|
|
61
79
|
}
|
|
62
80
|
}
|
|
63
81
|
return result;
|
package/dist/model.js
CHANGED
|
@@ -1,26 +1,18 @@
|
|
|
1
1
|
import { createOpenAI, openai as defaultOpenai } from '@ai-sdk/openai';
|
|
2
2
|
import { createAnthropic, anthropic } from '@ai-sdk/anthropic';
|
|
3
3
|
import { createDeepSeek, deepSeek } from '@ai-sdk/deepseek';
|
|
4
|
+
import { createGoogle, google as defaultGoogle } from '@ai-sdk/google';
|
|
4
5
|
import { expandModelString } from './auto-model.js';
|
|
5
|
-
import { CREDITS_API_KEY_PLACEHOLDER, INTEGRATIONS_TOKEN_HEADER, creditsProviderBaseUrl, resolveCreditsAuthConfig, shouldUseCreditsAuth, } from '@meetopenbot/plugin-sdk';
|
|
6
|
-
function googleChat(modelId) {
|
|
7
|
-
return createOpenAI({
|
|
8
|
-
baseURL: 'https://generativelanguage.googleapis.com/v1beta/openai',
|
|
9
|
-
apiKey: process.env.GOOGLE_GENERATIVE_AI_API_KEY,
|
|
10
|
-
}).chat(modelId);
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* OpenAI-compatible Chat Completions. Gemini's gateway only speaks this.
|
|
14
|
-
* Do not use for OpenAI itself — gpt-5.6-luna rejects function tools on
|
|
15
|
-
* `/v1/chat/completions` unless `reasoning_effort` is `none`.
|
|
16
|
-
*/
|
|
17
|
-
function openAiChatModel(options, modelId) {
|
|
18
|
-
return createOpenAI(options).chat(modelId);
|
|
19
|
-
}
|
|
6
|
+
import { CREDITS_API_KEY_PLACEHOLDER, INTEGRATIONS_TOKEN_HEADER, creditsProviderBaseUrl, resolveByokApiKey, resolveCreditsAuthConfig, shouldUseCreditsAuth, } from '@meetopenbot/plugin-sdk';
|
|
20
7
|
/** AI SDK default: OpenAI Responses API (`/v1/responses`), required for tools on Luna. */
|
|
21
8
|
function openAiResponsesModel(options, modelId) {
|
|
22
9
|
return createOpenAI(options)(modelId);
|
|
23
10
|
}
|
|
11
|
+
/** `@ai-sdk/google` only reads `GOOGLE_GENERATIVE_AI_API_KEY`; accept `GEMINI_API_KEY` too. */
|
|
12
|
+
function googleByok(modelId) {
|
|
13
|
+
const apiKey = resolveByokApiKey('google');
|
|
14
|
+
return apiKey ? createGoogle({ apiKey })(modelId) : defaultGoogle(modelId);
|
|
15
|
+
}
|
|
24
16
|
function resolveCreditsProvider(provider, modelId) {
|
|
25
17
|
const config = resolveCreditsAuthConfig();
|
|
26
18
|
if (!config) {
|
|
@@ -37,7 +29,7 @@ function resolveCreditsProvider(provider, modelId) {
|
|
|
37
29
|
case 'anthropic':
|
|
38
30
|
return createAnthropic(options)(modelId);
|
|
39
31
|
case 'google':
|
|
40
|
-
return
|
|
32
|
+
return createGoogle(options)(modelId);
|
|
41
33
|
case 'deepseek':
|
|
42
34
|
return createDeepSeek(options)(modelId);
|
|
43
35
|
}
|
|
@@ -56,7 +48,7 @@ export function resolveModel(modelString, options) {
|
|
|
56
48
|
case 'anthropic':
|
|
57
49
|
return useCredits ? resolveCreditsProvider('anthropic', modelId) : anthropic(modelId);
|
|
58
50
|
case 'google':
|
|
59
|
-
return useCredits ? resolveCreditsProvider('google', modelId) :
|
|
51
|
+
return useCredits ? resolveCreditsProvider('google', modelId) : googleByok(modelId);
|
|
60
52
|
case 'deepseek':
|
|
61
53
|
return useCredits ? resolveCreditsProvider('deepseek', modelId) : deepSeek(modelId);
|
|
62
54
|
default:
|
|
@@ -66,16 +58,9 @@ export function resolveModel(modelString, options) {
|
|
|
66
58
|
function providerOf(modelString) {
|
|
67
59
|
return expandModelString(modelString).split('/')[0] ?? '';
|
|
68
60
|
}
|
|
69
|
-
/** Low thinking for DeepSeek (Auto).
|
|
61
|
+
/** Low thinking for DeepSeek (Auto). Other providers use their SDK defaults. */
|
|
70
62
|
export function reasoningForModel(modelString) {
|
|
71
|
-
|
|
72
|
-
if (provider === 'deepseek')
|
|
63
|
+
if (providerOf(modelString) === 'deepseek')
|
|
73
64
|
return 'low';
|
|
74
|
-
if (provider === 'google')
|
|
75
|
-
return 'none';
|
|
76
65
|
return undefined;
|
|
77
66
|
}
|
|
78
|
-
/** Gemini's OpenAI Chat path drops thinking; omit parts so tool turns stay valid. */
|
|
79
|
-
export function includeReasoningInHistory(modelString) {
|
|
80
|
-
return providerOf(modelString) !== 'google';
|
|
81
|
-
}
|
package/dist/runtime.js
CHANGED
|
@@ -5,7 +5,7 @@ import { buildContext } from './context.js';
|
|
|
5
5
|
import { OPENBOT_SYSTEM_PROMPT } from './system-prompt.js';
|
|
6
6
|
import { isAuthErrorMessage, isCreditsErrorMessage } from '@meetopenbot/plugin-sdk';
|
|
7
7
|
import { AUTO_MODEL_ID, isAutoModel, isRetryableModelError, listAutoModelCandidates, missingByokApiKeyMessage, } from './auto-model.js';
|
|
8
|
-
import {
|
|
8
|
+
import { reasoningForModel, resolveModel } from './model.js';
|
|
9
9
|
import { fetchModelRegistry, getProviderModelOptions, listApiKeyProviders, PROVIDER_API_KEY_LINKS, } from '@meetopenbot/plugin-sdk';
|
|
10
10
|
import { errorFromStreamPart } from './stream-error.js';
|
|
11
11
|
import { createAgentOutputBuffer, isOutputBoundaryPart } from './output-buffer.js';
|
|
@@ -125,9 +125,7 @@ export async function* runOpenbotTurn(args, options) {
|
|
|
125
125
|
};
|
|
126
126
|
const tools = toAiTools(toolDefinitions, args, flushPendingOutput);
|
|
127
127
|
try {
|
|
128
|
-
let messages = eventsToModelMessages(events
|
|
129
|
-
includeReasoning: includeReasoningInHistory(candidate),
|
|
130
|
-
});
|
|
128
|
+
let messages = eventsToModelMessages(events);
|
|
131
129
|
if (hasImages) {
|
|
132
130
|
messages = await addImageParts(messages, events, async (name) => {
|
|
133
131
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meetopenbot/openbot",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "OpenBot coordinator runtime: ask specialists, route work into Spaces, and track todos.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@ai-sdk/anthropic": "^4.0.42",
|
|
23
23
|
"@ai-sdk/deepseek": "^3.0.32",
|
|
24
|
+
"@ai-sdk/google": "^4.0.58",
|
|
24
25
|
"@ai-sdk/openai": "^4.0.46",
|
|
25
26
|
"ai": "^7.0.77",
|
|
26
27
|
"zod": "^4.3.5",
|