@uncensoredcode/openbridge 0.1.1 → 0.1.2
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/package.json +1 -1
- package/packages/runtime/dist/tool-name-aliases.js +2 -1
- package/packages/server/dist/bridge/chat-completions/chat-completion-service.js +24 -1
- package/packages/server/dist/bridge/stores/local-session-package-store.js +49 -2
- package/packages/server/dist/bridge/stores/session-package-store.d.ts +2 -0
- package/packages/server/dist/bridge/stores/session-package-store.js +58 -1
- package/packages/server/dist/cli/index.d.ts +9 -0
- package/packages/server/dist/cli/index.js +1 -0
- package/packages/server/dist/cli/run-bridge-server-cli.d.ts +11 -0
- package/packages/server/dist/cli/run-bridge-server-cli.js +17 -5
package/package.json
CHANGED
|
@@ -36,7 +36,7 @@ const EXPLICIT_CHAT_COMPLETION_HEADER_KEYS = [
|
|
|
36
36
|
"x-thread-id"
|
|
37
37
|
];
|
|
38
38
|
async function handleBridgeChatCompletionRequest(input) {
|
|
39
|
-
const body = input.body;
|
|
39
|
+
const body = normalizeRecoverableChatCompletionRequest(input.body);
|
|
40
40
|
assertSupportedChatCompletionRequest(body);
|
|
41
41
|
const resolvedModel = resolveBridgeModel(input.providerStore.list(), body.model);
|
|
42
42
|
if (!resolvedModel) {
|
|
@@ -300,6 +300,29 @@ const chatCompletionsRequestSchema = z
|
|
|
300
300
|
metadata: z.record(z.string(), z.unknown()).optional()
|
|
301
301
|
})
|
|
302
302
|
.strict();
|
|
303
|
+
function normalizeRecoverableChatCompletionRequest(body) {
|
|
304
|
+
const messages = normalizeRecoverableChatCompletionMessages(body.messages);
|
|
305
|
+
return messages === body.messages
|
|
306
|
+
? body
|
|
307
|
+
: {
|
|
308
|
+
...body,
|
|
309
|
+
messages
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function normalizeRecoverableChatCompletionMessages(messages) {
|
|
313
|
+
const normalized = [];
|
|
314
|
+
let changed = false;
|
|
315
|
+
for (const message of messages) {
|
|
316
|
+
const previous = normalized.at(-1);
|
|
317
|
+
if (message.role === "user" && previous?.role === "user") {
|
|
318
|
+
normalized[normalized.length - 1] = message;
|
|
319
|
+
changed = true;
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
normalized.push(message);
|
|
323
|
+
}
|
|
324
|
+
return changed ? normalized : messages;
|
|
325
|
+
}
|
|
303
326
|
function assertSupportedChatCompletionRequest(body) {
|
|
304
327
|
if (body.n !== undefined && body.n !== 1) {
|
|
305
328
|
throw unsupportedChatCompletionsRequest("n", "Only n=1 is currently supported by the standalone bridge chat completions endpoint.");
|
|
@@ -5,7 +5,7 @@ import { z } from "zod";
|
|
|
5
5
|
import { bridgeApiErrorModule } from "../../shared/bridge-api-error.js";
|
|
6
6
|
import { providerStoreModule } from "./provider-store.js";
|
|
7
7
|
import { sessionPackageStoreModule } from "./session-package-store.js";
|
|
8
|
-
const { buildSessionPackageMetadata, cloneConfig, cloneInstalledProviderPackage, cloneProvider, cloneSessionPackage, cloneSessionPackageMetadata, inferProviderFromSessionPackage, installedProviderPackageSchema, sessionPackageDeleteResponseSchema, sessionPackageSchema, sessionPackageMetadataSchema } = sessionPackageStoreModule;
|
|
8
|
+
const { buildQwenConversationRequestBody, buildSessionPackageMetadata, cloneConfig, cloneInstalledProviderPackage, cloneProvider, cloneSessionPackage, cloneSessionPackageMetadata, inferProviderFromSessionPackage, installedProviderPackageSchema, sessionPackageDeleteResponseSchema, sessionPackageSchema, sessionPackageMetadataSchema } = sessionPackageStoreModule;
|
|
9
9
|
const { createProviderRequestSchema, providerDeleteResponseSchema, providerSchema } = providerStoreModule;
|
|
10
10
|
const { BridgeApiError } = bridgeApiErrorModule;
|
|
11
11
|
const SESSION_VAULT_VERSION = 1;
|
|
@@ -315,7 +315,7 @@ function createLocalSessionPackageStore(options) {
|
|
|
315
315
|
return null;
|
|
316
316
|
}
|
|
317
317
|
const entry = readVaultEntry(metadata);
|
|
318
|
-
return decryptInstalledPackage(entry, key);
|
|
318
|
+
return normalizeInstalledProviderPackage(decryptInstalledPackage(entry, key));
|
|
319
319
|
}
|
|
320
320
|
function readVaultIndex() {
|
|
321
321
|
if (!existsSync(indexPath)) {
|
|
@@ -372,6 +372,53 @@ function createLocalSessionPackageStore(options) {
|
|
|
372
372
|
return path.join(entriesPath, `${handle}.json`);
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
|
+
function normalizeInstalledProviderPackage(installed) {
|
|
376
|
+
const transport = readInstalledQwenTransport(installed.provider.config);
|
|
377
|
+
if (!transport) {
|
|
378
|
+
return installed;
|
|
379
|
+
}
|
|
380
|
+
const request = readInstalledTransportRequest(transport);
|
|
381
|
+
if (!request || !("body" in request)) {
|
|
382
|
+
return installed;
|
|
383
|
+
}
|
|
384
|
+
const normalizedBody = buildQwenConversationRequestBody(request.body);
|
|
385
|
+
if (JSON.stringify(normalizedBody) === JSON.stringify(request.body)) {
|
|
386
|
+
return installed;
|
|
387
|
+
}
|
|
388
|
+
return installedProviderPackageSchema.parse({
|
|
389
|
+
provider: {
|
|
390
|
+
...cloneProvider(installed.provider),
|
|
391
|
+
config: {
|
|
392
|
+
...cloneConfig(installed.provider.config),
|
|
393
|
+
transport: {
|
|
394
|
+
...transport,
|
|
395
|
+
request: {
|
|
396
|
+
...request,
|
|
397
|
+
body: normalizedBody
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
session: installed.session ? cloneSessionPackage(installed.session) : null
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
function readInstalledQwenTransport(config) {
|
|
406
|
+
const transport = typeof config.transport === "object" &&
|
|
407
|
+
config.transport !== null &&
|
|
408
|
+
!Array.isArray(config.transport)
|
|
409
|
+
? config.transport
|
|
410
|
+
: null;
|
|
411
|
+
const request = readInstalledTransportRequest(transport);
|
|
412
|
+
const url = typeof request?.url === "string" ? request.url.trim() : "";
|
|
413
|
+
return /^https:\/\/chat\.qwen\.ai\/api\/v2\/chat\/completions\b/u.test(url) ? transport : null;
|
|
414
|
+
}
|
|
415
|
+
function readInstalledTransportRequest(transport) {
|
|
416
|
+
return typeof transport?.request === "object" &&
|
|
417
|
+
transport.request !== null &&
|
|
418
|
+
!Array.isArray(transport.request)
|
|
419
|
+
? transport.request
|
|
420
|
+
: null;
|
|
421
|
+
}
|
|
375
422
|
function encryptInstalledPackage(metadata, value, key) {
|
|
376
423
|
const iv = crypto.randomBytes(12);
|
|
377
424
|
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
@@ -165,6 +165,7 @@ declare function inferProviderFromSessionPackage(input: {
|
|
|
165
165
|
createdAt: string;
|
|
166
166
|
updatedAt: string;
|
|
167
167
|
};
|
|
168
|
+
declare function buildQwenConversationRequestBody(requestBody: unknown): unknown;
|
|
168
169
|
export declare const sessionPackageStoreModule: {
|
|
169
170
|
sessionPackageSchema: z.ZodObject<{
|
|
170
171
|
schemaVersion: z.ZodOptional<z.ZodNumber>;
|
|
@@ -274,6 +275,7 @@ export declare const sessionPackageStoreModule: {
|
|
|
274
275
|
}, z.core.$strict>>;
|
|
275
276
|
}, z.core.$strict>;
|
|
276
277
|
createInMemorySessionPackageStore: typeof createInMemorySessionPackageStore;
|
|
278
|
+
buildQwenConversationRequestBody: typeof buildQwenConversationRequestBody;
|
|
277
279
|
buildSessionPackageStatus: typeof buildSessionPackageStatus;
|
|
278
280
|
cloneProvider: typeof cloneProvider;
|
|
279
281
|
cloneConfig: typeof cloneConfig;
|
|
@@ -416,7 +416,9 @@ function inferQwenConversationTransport(selectedRequest, value) {
|
|
|
416
416
|
method: String(selectedRequest.method).toUpperCase(),
|
|
417
417
|
url: "https://chat.qwen.ai/api/v2/chat/completions?chat_id={{conversationId}}",
|
|
418
418
|
headers: {},
|
|
419
|
-
...(requestBody === undefined
|
|
419
|
+
...(requestBody === undefined
|
|
420
|
+
? {}
|
|
421
|
+
: { body: buildQwenConversationRequestBody(requestBody) })
|
|
420
422
|
},
|
|
421
423
|
response: {
|
|
422
424
|
contentPaths: DEFAULT_SSE_CONTENT_PATHS,
|
|
@@ -926,6 +928,60 @@ function buildQwenBootstrapRequestBody(selectedRequest) {
|
|
|
926
928
|
project_id: projectId
|
|
927
929
|
};
|
|
928
930
|
}
|
|
931
|
+
function buildQwenConversationRequestBody(requestBody) {
|
|
932
|
+
if (typeof requestBody !== "object" || requestBody === null || Array.isArray(requestBody)) {
|
|
933
|
+
return requestBody;
|
|
934
|
+
}
|
|
935
|
+
const base = structuredClone(requestBody);
|
|
936
|
+
if ("chat_id" in base) {
|
|
937
|
+
base.chat_id = "{{conversationId}}";
|
|
938
|
+
}
|
|
939
|
+
if ("model" in base) {
|
|
940
|
+
base.model = "{{modelId}}";
|
|
941
|
+
}
|
|
942
|
+
if ("timestamp" in base) {
|
|
943
|
+
base.timestamp = "{{unixTimestampSec}}";
|
|
944
|
+
}
|
|
945
|
+
if ("parent_id" in base) {
|
|
946
|
+
base.parent_id = "{{parentIdOrNull}}";
|
|
947
|
+
}
|
|
948
|
+
if (Array.isArray(base.messages) && base.messages.length > 0) {
|
|
949
|
+
base.messages = base.messages.map((message, index) => index === 0 ? buildQwenConversationUserMessage(message) : message);
|
|
950
|
+
}
|
|
951
|
+
return base;
|
|
952
|
+
}
|
|
953
|
+
function buildQwenConversationUserMessage(message) {
|
|
954
|
+
if (typeof message !== "object" || message === null || Array.isArray(message)) {
|
|
955
|
+
return {
|
|
956
|
+
fid: "{{messageId}}",
|
|
957
|
+
role: "user",
|
|
958
|
+
content: "{{prompt}}"
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
const nextMessage = structuredClone(message);
|
|
962
|
+
nextMessage.fid = "{{messageId}}";
|
|
963
|
+
nextMessage.role = "user";
|
|
964
|
+
nextMessage.content = "{{prompt}}";
|
|
965
|
+
if ("timestamp" in nextMessage) {
|
|
966
|
+
nextMessage.timestamp = "{{unixTimestampSec}}";
|
|
967
|
+
}
|
|
968
|
+
if ("models" in nextMessage) {
|
|
969
|
+
nextMessage.models = ["{{modelId}}"];
|
|
970
|
+
}
|
|
971
|
+
if ("parentId" in nextMessage) {
|
|
972
|
+
nextMessage.parentId = "{{parentIdOrNull}}";
|
|
973
|
+
}
|
|
974
|
+
if ("parent_id" in nextMessage) {
|
|
975
|
+
nextMessage.parent_id = "{{parentIdOrNull}}";
|
|
976
|
+
}
|
|
977
|
+
if ("childrenIds" in nextMessage && Array.isArray(nextMessage.childrenIds)) {
|
|
978
|
+
nextMessage.childrenIds = [];
|
|
979
|
+
}
|
|
980
|
+
if ("children_ids" in nextMessage && Array.isArray(nextMessage.children_ids)) {
|
|
981
|
+
nextMessage.children_ids = [];
|
|
982
|
+
}
|
|
983
|
+
return nextMessage;
|
|
984
|
+
}
|
|
929
985
|
function buildOpenAiConversationRequestBody(requestBody) {
|
|
930
986
|
const base = requestBody ? structuredClone(requestBody) : {};
|
|
931
987
|
const capturedMessage = Array.isArray(requestBody?.messages) &&
|
|
@@ -1516,6 +1572,7 @@ export const sessionPackageStoreModule = {
|
|
|
1516
1572
|
sessionPackageMetadataSchema,
|
|
1517
1573
|
installedProviderPackageSchema,
|
|
1518
1574
|
createInMemorySessionPackageStore,
|
|
1575
|
+
buildQwenConversationRequestBody,
|
|
1519
1576
|
buildSessionPackageStatus,
|
|
1520
1577
|
cloneProvider,
|
|
1521
1578
|
cloneConfig,
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
export declare const cliModule: {
|
|
2
|
+
buildDetachedServerLaunchCommand: (input: {
|
|
3
|
+
scriptPath: string;
|
|
4
|
+
argv: string[];
|
|
5
|
+
execPath: string;
|
|
6
|
+
execArgv: string[];
|
|
7
|
+
}) => {
|
|
8
|
+
command: string;
|
|
9
|
+
args: string[];
|
|
10
|
+
};
|
|
2
11
|
getBridgeServerCliHelpText: () => string;
|
|
3
12
|
parseBridgeServerCliArgs: (input: {
|
|
4
13
|
argv: string[];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { runBridgeServerCliModule } from "./run-bridge-server-cli.js";
|
|
2
2
|
export const cliModule = {
|
|
3
|
+
buildDetachedServerLaunchCommand: runBridgeServerCliModule.buildDetachedServerLaunchCommand,
|
|
3
4
|
getBridgeServerCliHelpText: runBridgeServerCliModule.getBridgeServerCliHelpText,
|
|
4
5
|
parseBridgeServerCliArgs: runBridgeServerCliModule.parseBridgeServerCliArgs,
|
|
5
6
|
runBridgeServerCli: runBridgeServerCliModule.runBridgeServerCli
|
|
@@ -121,13 +121,24 @@ type SpawnDetachedServerProcessInput = {
|
|
|
121
121
|
type SpawnDetachedServerProcessResult = {
|
|
122
122
|
pid: number;
|
|
123
123
|
};
|
|
124
|
+
type DetachedServerLaunchCommand = {
|
|
125
|
+
command: string;
|
|
126
|
+
args: string[];
|
|
127
|
+
};
|
|
124
128
|
declare function runBridgeServerCli(input: RunBridgeServerCliInput): Promise<number>;
|
|
125
129
|
declare function parseBridgeServerCliArgs(input: {
|
|
126
130
|
argv: string[];
|
|
127
131
|
env?: NodeJS.ProcessEnv;
|
|
128
132
|
}): BridgeServerCliCommand;
|
|
129
133
|
declare function getBridgeServerCliHelpText(): string;
|
|
134
|
+
declare function buildDetachedServerLaunchCommand(input: {
|
|
135
|
+
scriptPath: string;
|
|
136
|
+
argv: string[];
|
|
137
|
+
execPath: string;
|
|
138
|
+
execArgv: string[];
|
|
139
|
+
}): DetachedServerLaunchCommand;
|
|
130
140
|
export declare const runBridgeServerCliModule: {
|
|
141
|
+
buildDetachedServerLaunchCommand: typeof buildDetachedServerLaunchCommand;
|
|
131
142
|
runBridgeServerCli: typeof runBridgeServerCli;
|
|
132
143
|
parseBridgeServerCliArgs: typeof parseBridgeServerCliArgs;
|
|
133
144
|
getBridgeServerCliHelpText: typeof getBridgeServerCliHelpText;
|
|
@@ -99,7 +99,8 @@ async function runBridgeServerCli(input) {
|
|
|
99
99
|
}
|
|
100
100
|
if (command.kind === "status") {
|
|
101
101
|
const status = await getServerStatus(command.config, input.fetchImpl);
|
|
102
|
-
|
|
102
|
+
const { logPath: _logPath, ...statusOutput } = status;
|
|
103
|
+
writeJson(stdout, statusOutput);
|
|
103
104
|
return status.running && status.healthy !== false ? 0 : 1;
|
|
104
105
|
}
|
|
105
106
|
if (command.kind === "stop") {
|
|
@@ -271,13 +272,11 @@ async function runBridgeServerCli(input) {
|
|
|
271
272
|
const statusMessage = readyState === null
|
|
272
273
|
? [
|
|
273
274
|
`Bridge server started in background (pid ${daemon.pid}).`,
|
|
274
|
-
`Logs: ${processFiles.logPath}`,
|
|
275
275
|
`Startup is still pending; check status with "openbridge status".`
|
|
276
276
|
].join("\n")
|
|
277
277
|
: [
|
|
278
278
|
`Bridge server started in background (pid ${readyState.pid}).`,
|
|
279
|
-
`Base URL: ${readyState.baseUrl}
|
|
280
|
-
`Logs: ${readyState.logPath}`
|
|
279
|
+
`Base URL: ${readyState.baseUrl}`
|
|
281
280
|
].join("\n");
|
|
282
281
|
stdout.write(`${statusMessage}\n`);
|
|
283
282
|
return 0;
|
|
@@ -1178,7 +1177,13 @@ async function defaultSpawnDetachedServerProcess(input) {
|
|
|
1178
1177
|
});
|
|
1179
1178
|
const logHandle = await open(input.logPath, "a", 0o600);
|
|
1180
1179
|
try {
|
|
1181
|
-
const
|
|
1180
|
+
const launch = buildDetachedServerLaunchCommand({
|
|
1181
|
+
scriptPath,
|
|
1182
|
+
argv: input.argv,
|
|
1183
|
+
execPath: process.execPath,
|
|
1184
|
+
execArgv: process.execArgv
|
|
1185
|
+
});
|
|
1186
|
+
const child = spawn(launch.command, launch.args, {
|
|
1182
1187
|
cwd: input.cwd,
|
|
1183
1188
|
env: input.env,
|
|
1184
1189
|
detached: true,
|
|
@@ -1196,6 +1201,12 @@ async function defaultSpawnDetachedServerProcess(input) {
|
|
|
1196
1201
|
await logHandle.close();
|
|
1197
1202
|
}
|
|
1198
1203
|
}
|
|
1204
|
+
function buildDetachedServerLaunchCommand(input) {
|
|
1205
|
+
return {
|
|
1206
|
+
command: input.execPath,
|
|
1207
|
+
args: [...input.execArgv, input.scriptPath, ...input.argv]
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1199
1210
|
function toForegroundStartArgv(argv) {
|
|
1200
1211
|
const normalized = argv.length === 0 || argv[0]?.startsWith("--") ? ["start", ...argv] : [...argv];
|
|
1201
1212
|
if (normalized[0] !== "start") {
|
|
@@ -1280,6 +1291,7 @@ function requireConfigPath(value, key) {
|
|
|
1280
1291
|
return value;
|
|
1281
1292
|
}
|
|
1282
1293
|
export const runBridgeServerCliModule = {
|
|
1294
|
+
buildDetachedServerLaunchCommand,
|
|
1283
1295
|
runBridgeServerCli,
|
|
1284
1296
|
parseBridgeServerCliArgs,
|
|
1285
1297
|
getBridgeServerCliHelpText
|