@plico-ai/runtime 0.0.0-reserved.0 → 0.1.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/NOTICE +4 -0
- package/README.md +9 -3
- package/dist/artifacts.d.ts +10 -0
- package/dist/artifacts.js +106 -0
- package/dist/engine/context.d.ts +16 -0
- package/dist/engine/context.js +77 -0
- package/dist/engine/engine.d.ts +12 -0
- package/dist/engine/engine.js +235 -0
- package/dist/engine/errors.d.ts +12 -0
- package/dist/engine/errors.js +19 -0
- package/dist/engine/index.d.ts +7 -0
- package/dist/engine/index.js +5 -0
- package/dist/engine/journal.d.ts +65 -0
- package/dist/engine/journal.js +1 -0
- package/dist/engine/memory-journal.d.ts +2 -0
- package/dist/engine/memory-journal.js +125 -0
- package/dist/engine/sqlite-engine.d.ts +7 -0
- package/dist/engine/sqlite-engine.js +6 -0
- package/dist/engine/sqlite-journal.d.ts +5 -0
- package/dist/engine/sqlite-journal.js +136 -0
- package/dist/engine/types.d.ts +97 -0
- package/dist/engine/types.js +1 -0
- package/dist/env.d.ts +7 -0
- package/dist/env.js +19 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.js +40 -0
- package/dist/evals/discover.d.ts +3 -0
- package/dist/evals/discover.js +49 -0
- package/dist/evals/index.d.ts +3 -0
- package/dist/evals/index.js +3 -0
- package/dist/evals/promote.d.ts +7 -0
- package/dist/evals/promote.js +32 -0
- package/dist/evals/run-evals.d.ts +2 -0
- package/dist/evals/run-evals.js +69 -0
- package/dist/events.d.ts +28 -0
- package/dist/events.js +104 -0
- package/dist/guards.d.ts +1 -0
- package/dist/guards.js +3 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +17 -0
- package/dist/instructions.d.ts +4 -0
- package/dist/instructions.js +23 -0
- package/dist/policy/approval.d.ts +8 -0
- package/dist/policy/approval.js +27 -0
- package/dist/providers/ai-sdk.d.ts +10 -0
- package/dist/providers/ai-sdk.js +154 -0
- package/dist/providers/configured.d.ts +17 -0
- package/dist/providers/configured.js +60 -0
- package/dist/providers/openai-compatible.d.ts +8 -0
- package/dist/providers/openai-compatible.js +14 -0
- package/dist/providers/project-module.d.ts +7 -0
- package/dist/providers/project-module.js +29 -0
- package/dist/providers/registry.d.ts +20 -0
- package/dist/providers/registry.js +53 -0
- package/dist/readiness/check-readiness.d.ts +2 -0
- package/dist/readiness/check-readiness.js +33 -0
- package/dist/readiness/checks/approval-policy.d.ts +5 -0
- package/dist/readiness/checks/approval-policy.js +19 -0
- package/dist/readiness/checks/artifact-access.d.ts +3 -0
- package/dist/readiness/checks/artifact-access.js +5 -0
- package/dist/readiness/checks/artifacts.d.ts +6 -0
- package/dist/readiness/checks/artifacts.js +22 -0
- package/dist/readiness/checks/env.d.ts +3 -0
- package/dist/readiness/checks/env.js +10 -0
- package/dist/readiness/checks/evals.d.ts +2 -0
- package/dist/readiness/checks/evals.js +53 -0
- package/dist/readiness/checks/persistence.d.ts +3 -0
- package/dist/readiness/checks/persistence.js +61 -0
- package/dist/readiness/checks/project.d.ts +7 -0
- package/dist/readiness/checks/project.js +21 -0
- package/dist/readiness/checks/provider.d.ts +2 -0
- package/dist/readiness/checks/provider.js +20 -0
- package/dist/readiness/checks/server-config.d.ts +2 -0
- package/dist/readiness/checks/server-config.js +13 -0
- package/dist/readiness/checks/studio.d.ts +2 -0
- package/dist/readiness/checks/studio.js +16 -0
- package/dist/readiness/checks/thread-store.d.ts +3 -0
- package/dist/readiness/checks/thread-store.js +12 -0
- package/dist/readiness/checks/tools.d.ts +3 -0
- package/dist/readiness/checks/tools.js +42 -0
- package/dist/readiness/diagnostic.d.ts +2 -0
- package/dist/readiness/diagnostic.js +7 -0
- package/dist/readiness/index.d.ts +1 -0
- package/dist/readiness/index.js +1 -0
- package/dist/readiness/open-database.d.ts +8 -0
- package/dist/readiness/open-database.js +15 -0
- package/dist/run/artifact-tool-context.d.ts +19 -0
- package/dist/run/artifact-tool-context.js +74 -0
- package/dist/run/finalize.d.ts +16 -0
- package/dist/run/finalize.js +11 -0
- package/dist/run/index.d.ts +1 -0
- package/dist/run/index.js +1 -0
- package/dist/run/pinned-instructions.d.ts +18 -0
- package/dist/run/pinned-instructions.js +36 -0
- package/dist/run/record-tools.d.ts +8 -0
- package/dist/run/record-tools.js +21 -0
- package/dist/run/run-project.d.ts +17 -0
- package/dist/run/run-project.js +222 -0
- package/dist/run/strategies/approval.d.ts +9 -0
- package/dist/run/strategies/approval.js +42 -0
- package/dist/run/strategies/default-dry.d.ts +3 -0
- package/dist/run/strategies/default-dry.js +5 -0
- package/dist/run/strategies/provider.d.ts +6 -0
- package/dist/run/strategies/provider.js +193 -0
- package/dist/run/strategies/scripted.d.ts +5 -0
- package/dist/run/strategies/scripted.js +106 -0
- package/dist/run/tool-fingerprint.d.ts +2 -0
- package/dist/run/tool-fingerprint.js +26 -0
- package/dist/run/tool-step.d.ts +4 -0
- package/dist/run/tool-step.js +62 -0
- package/dist/run/tracing.d.ts +12 -0
- package/dist/run/tracing.js +92 -0
- package/dist/script.d.ts +4 -0
- package/dist/script.js +51 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +1 -0
- package/dist/storage/metadata.d.ts +5 -0
- package/dist/storage/metadata.js +26 -0
- package/dist/storage/sqlite/append-event.d.ts +3 -0
- package/dist/storage/sqlite/append-event.js +108 -0
- package/dist/storage/sqlite/driver.d.ts +9 -0
- package/dist/storage/sqlite/driver.js +13 -0
- package/dist/storage/sqlite/index.d.ts +1 -0
- package/dist/storage/sqlite/index.js +1 -0
- package/dist/storage/sqlite/open.d.ts +3 -0
- package/dist/storage/sqlite/open.js +54 -0
- package/dist/storage/sqlite/owner-lock.d.ts +5 -0
- package/dist/storage/sqlite/owner-lock.js +55 -0
- package/dist/storage/sqlite/project-thread.d.ts +4 -0
- package/dist/storage/sqlite/project-thread.js +33 -0
- package/dist/storage/sqlite/queries.d.ts +9 -0
- package/dist/storage/sqlite/queries.js +134 -0
- package/dist/storage/sqlite/recover-interrupted.d.ts +2 -0
- package/dist/storage/sqlite/recover-interrupted.js +46 -0
- package/dist/storage/sqlite/schema.d.ts +10 -0
- package/dist/storage/sqlite/schema.js +205 -0
- package/dist/storage/sqlite/store.d.ts +2 -0
- package/dist/storage/sqlite/store.js +51 -0
- package/dist/storage/sqlite/thread-log.d.ts +4 -0
- package/dist/storage/sqlite/thread-log.js +9 -0
- package/dist/storage/sqlite/thread-title.d.ts +24 -0
- package/dist/storage/sqlite/thread-title.js +85 -0
- package/dist/storage/sqlite/threads.d.ts +47 -0
- package/dist/storage/sqlite/threads.js +156 -0
- package/dist/threads/stopwords.d.ts +1 -0
- package/dist/threads/stopwords.js +16 -0
- package/dist/threads/suggest-title.d.ts +9 -0
- package/dist/threads/suggest-title.js +102 -0
- package/dist/threads/titler.d.ts +10 -0
- package/dist/threads/titler.js +63 -0
- package/dist/threads/words.d.ts +12 -0
- package/dist/threads/words.js +39 -0
- package/dist/tools/artifact-model-tool-operations.d.ts +29 -0
- package/dist/tools/artifact-model-tool-operations.js +124 -0
- package/dist/tools/artifact-model-tools.d.ts +2 -0
- package/dist/tools/artifact-model-tools.js +101 -0
- package/dist/tools/discover.d.ts +4 -0
- package/dist/tools/discover.js +34 -0
- package/dist/tools/index.d.ts +3 -0
- package/dist/tools/index.js +3 -0
- package/dist/tools/load.d.ts +2 -0
- package/dist/tools/load.js +83 -0
- package/dist/tools/normalize.d.ts +2 -0
- package/dist/tools/normalize.js +48 -0
- package/dist/tools/schema.d.ts +7 -0
- package/dist/tools/schema.js +101 -0
- package/dist/tools/skill-model-tool.d.ts +7 -0
- package/dist/tools/skill-model-tool.js +38 -0
- package/dist/types/artifacts.d.ts +5 -0
- package/dist/types/artifacts.js +1 -0
- package/dist/types/evals.d.ts +16 -0
- package/dist/types/evals.js +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.js +1 -0
- package/dist/types/provider.d.ts +45 -0
- package/dist/types/provider.js +1 -0
- package/dist/types/readiness.d.ts +9 -0
- package/dist/types/readiness.js +1 -0
- package/dist/types/run.d.ts +28 -0
- package/dist/types/run.js +1 -0
- package/dist/types/script.d.ts +16 -0
- package/dist/types/script.js +1 -0
- package/dist/types/store.d.ts +24 -0
- package/dist/types/store.js +1 -0
- package/dist/types/tools.d.ts +15 -0
- package/dist/types/tools.js +1 -0
- package/package.json +67 -8
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { EventStreamType, PlicoEventPayloads } from "@plico-ai/contracts";
|
|
2
|
+
import type { EventStore, PlicoEvent, PlicoEventType } from "./types/index.js";
|
|
3
|
+
export declare const runStreamType: EventStreamType;
|
|
4
|
+
/**
|
|
5
|
+
* Build the terminal failure pair both recovery paths append. The caller supplies the tail so
|
|
6
|
+
* appending to an event store and inserting directly into SQLite share one sequencing scheme.
|
|
7
|
+
*/
|
|
8
|
+
export declare function runFailureEvents(runId: string, last: {
|
|
9
|
+
sequence: number;
|
|
10
|
+
type: string;
|
|
11
|
+
} | undefined, message: string, timestamp?: string): PlicoEvent[];
|
|
12
|
+
export declare class InMemoryEventLog {
|
|
13
|
+
#private;
|
|
14
|
+
private readonly runId;
|
|
15
|
+
private readonly store;
|
|
16
|
+
/**
|
|
17
|
+
* Opens the log of a run that may have started before. The loop emits the same events again when
|
|
18
|
+
* it replays; events already stored are matched, not written twice.
|
|
19
|
+
*/
|
|
20
|
+
static open(runId: string, store: EventStore | undefined): Promise<InMemoryEventLog>;
|
|
21
|
+
constructor(runId: string, store?: EventStore | undefined);
|
|
22
|
+
append<T extends PlicoEventType>(type: T, payload: PlicoEventPayloads[T]): Promise<PlicoEvent>;
|
|
23
|
+
/** Skip previously persisted live chunks when replaying a model step. */
|
|
24
|
+
skipModelDeltas(turn: number): void;
|
|
25
|
+
/** Continue after the recorded tail when recovery cannot replay the workflow. */
|
|
26
|
+
continueAfterReplay(): void;
|
|
27
|
+
all(): PlicoEvent[];
|
|
28
|
+
}
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { ProjectChangedError } from "./engine/index.js";
|
|
2
|
+
export const runStreamType = "run";
|
|
3
|
+
/**
|
|
4
|
+
* Build the terminal failure pair both recovery paths append. The caller supplies the tail so
|
|
5
|
+
* appending to an event store and inserting directly into SQLite share one sequencing scheme.
|
|
6
|
+
*/
|
|
7
|
+
export function runFailureEvents(runId, last, message, timestamp = new Date().toISOString()) {
|
|
8
|
+
const events = [];
|
|
9
|
+
let sequence = last?.sequence ?? 0;
|
|
10
|
+
if (last?.type !== "run.error") {
|
|
11
|
+
sequence += 1;
|
|
12
|
+
events.push({
|
|
13
|
+
schemaVersion: 1,
|
|
14
|
+
streamType: "run",
|
|
15
|
+
streamId: runId,
|
|
16
|
+
runId,
|
|
17
|
+
id: `evt-${String(sequence).padStart(4, "0")}`,
|
|
18
|
+
sequence,
|
|
19
|
+
timestamp,
|
|
20
|
+
type: "run.error",
|
|
21
|
+
payload: { phase: "runtime", message },
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
sequence += 1;
|
|
25
|
+
events.push({
|
|
26
|
+
schemaVersion: 1,
|
|
27
|
+
streamType: "run",
|
|
28
|
+
streamId: runId,
|
|
29
|
+
runId,
|
|
30
|
+
id: `evt-${String(sequence).padStart(4, "0")}`,
|
|
31
|
+
sequence,
|
|
32
|
+
timestamp,
|
|
33
|
+
type: "run.failed",
|
|
34
|
+
payload: { phase: "runtime" },
|
|
35
|
+
});
|
|
36
|
+
return events;
|
|
37
|
+
}
|
|
38
|
+
export class InMemoryEventLog {
|
|
39
|
+
runId;
|
|
40
|
+
store;
|
|
41
|
+
#events = [];
|
|
42
|
+
#replayed = 0;
|
|
43
|
+
#cursor = 0;
|
|
44
|
+
/**
|
|
45
|
+
* Opens the log of a run that may have started before. The loop emits the same events again when
|
|
46
|
+
* it replays; events already stored are matched, not written twice.
|
|
47
|
+
*/
|
|
48
|
+
static async open(runId, store) {
|
|
49
|
+
const log = new InMemoryEventLog(runId, store);
|
|
50
|
+
if (store)
|
|
51
|
+
log.#events.push(...(await store.getEvents(runId)));
|
|
52
|
+
log.#replayed = log.#events.length;
|
|
53
|
+
return log;
|
|
54
|
+
}
|
|
55
|
+
constructor(runId, store = undefined) {
|
|
56
|
+
this.runId = runId;
|
|
57
|
+
this.store = store;
|
|
58
|
+
}
|
|
59
|
+
async append(type, payload) {
|
|
60
|
+
if (this.#cursor < this.#replayed) {
|
|
61
|
+
const recorded = this.#events[this.#cursor];
|
|
62
|
+
if (recorded.type !== type || JSON.stringify(recorded.payload) !== JSON.stringify(payload)) {
|
|
63
|
+
// Later events (the failure) are appended after what the earlier attempt stored.
|
|
64
|
+
this.#cursor = this.#replayed;
|
|
65
|
+
throw new ProjectChangedError("event", this.#cursor, recorded.type, type);
|
|
66
|
+
}
|
|
67
|
+
this.#cursor += 1;
|
|
68
|
+
return recorded;
|
|
69
|
+
}
|
|
70
|
+
const sequence = this.#events.length + 1;
|
|
71
|
+
// TypeScript cannot prove this generic object is a union member; callers are checked against PlicoEventPayloads[T].
|
|
72
|
+
const event = {
|
|
73
|
+
schemaVersion: 1,
|
|
74
|
+
streamType: runStreamType,
|
|
75
|
+
streamId: this.runId,
|
|
76
|
+
runId: this.runId,
|
|
77
|
+
id: `evt-${String(sequence).padStart(4, "0")}`,
|
|
78
|
+
sequence,
|
|
79
|
+
timestamp: new Date().toISOString(),
|
|
80
|
+
type,
|
|
81
|
+
payload,
|
|
82
|
+
};
|
|
83
|
+
await this.store?.appendEvent(event);
|
|
84
|
+
this.#events.push(event);
|
|
85
|
+
return event;
|
|
86
|
+
}
|
|
87
|
+
/** Skip previously persisted live chunks when replaying a model step. */
|
|
88
|
+
skipModelDeltas(turn) {
|
|
89
|
+
while (this.#cursor < this.#replayed) {
|
|
90
|
+
const event = this.#events[this.#cursor];
|
|
91
|
+
if ((event?.type !== "model.delta" && event?.type !== "model.attempt") ||
|
|
92
|
+
event.payload.turn !== turn)
|
|
93
|
+
break;
|
|
94
|
+
this.#cursor++;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Continue after the recorded tail when recovery cannot replay the workflow. */
|
|
98
|
+
continueAfterReplay() {
|
|
99
|
+
this.#cursor = this.#replayed;
|
|
100
|
+
}
|
|
101
|
+
all() {
|
|
102
|
+
return [...this.#events];
|
|
103
|
+
}
|
|
104
|
+
}
|
package/dist/guards.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
package/dist/guards.js
ADDED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { buildArtifactWorkspace, readArtifactSnapshot, readArtifactVersion } from "./artifacts.js";
|
|
2
|
+
export { createEngine, createMemoryJournal, createSqliteEngine, type Engine, type Journal, ProjectChangedError, type RecoveredRun, type RunContext, type Workflow, } from "./engine/index.js";
|
|
3
|
+
export { loadedEnvFiles, loadProjectEnv } from "./env.js";
|
|
4
|
+
export { type ModelErrorKind, ModelProviderError } from "./errors.js";
|
|
5
|
+
export { evalCaseFromRun, runEvals } from "./evals/index.js";
|
|
6
|
+
export { runFailureEvents } from "./events.js";
|
|
7
|
+
export { composeInstructions } from "./instructions.js";
|
|
8
|
+
export { createConfiguredModelProvider, modelProviderAccountKey, modelProviderRecoveryKey, supportsModelProvider, } from "./providers/configured.js";
|
|
9
|
+
export { createOpenAICompatibleProvider, type OpenAICompatibleProviderOptions, } from "./providers/openai-compatible.js";
|
|
10
|
+
export { checkReadiness } from "./readiness/index.js";
|
|
11
|
+
export { recoverRuns, runProject } from "./run/index.js";
|
|
12
|
+
export { normalizeScript, ScriptValidationError } from "./script.js";
|
|
13
|
+
export { createSqliteEventStore } from "./storage/index.js";
|
|
14
|
+
export { matchesMetadata } from "./storage/metadata.js";
|
|
15
|
+
export { createSqliteThreadStore, ThreadBusyError, type ThreadStore, } from "./storage/sqlite/threads.js";
|
|
16
|
+
export { createThreadTitler, type ThreadTitler } from "./threads/titler.js";
|
|
17
|
+
export { discoverTools } from "./tools/index.js";
|
|
18
|
+
export type { ArtifactContent, ArtifactFile, ArtifactFileInput, ArtifactFileMetadata, ArtifactSnapshot, ArtifactSnapshotDetail, ArtifactVersion, ArtifactWorkspace, ComposedInstructions, CreateSqliteEventStoreOptions, DeclaredTool, DiagnosticSeverity, DryRunScriptStep, EvalCase, EvalCaseResult, EvalExpectation, EvalReport, EvalSummary, EventStore, JsonSchema, ModelChunk, ModelProvider, ModelRequest, ModelResponse, ModelToolCall, PlicoEvent, PlicoEventType, ProviderMessage, ReadinessDiagnostic, ReadinessOptions, ReadinessReport, RunEvalsOptions, RunProjectOptions, RunResult, RunStatus, RunSummary, ToolApprovalPolicy, ToolArtifactContext, ToolArtifactFile, ToolDefinition, ToolHandlerContext, ToolInputParseResult, ToolInputSchema, ZodInputParseResult, ZodInputSchema, } from "./types/index.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { buildArtifactWorkspace, readArtifactSnapshot, readArtifactVersion } from "./artifacts.js";
|
|
2
|
+
export { createEngine, createMemoryJournal, createSqliteEngine, ProjectChangedError, } from "./engine/index.js";
|
|
3
|
+
export { loadedEnvFiles, loadProjectEnv } from "./env.js";
|
|
4
|
+
export { ModelProviderError } from "./errors.js";
|
|
5
|
+
export { evalCaseFromRun, runEvals } from "./evals/index.js";
|
|
6
|
+
export { runFailureEvents } from "./events.js";
|
|
7
|
+
export { composeInstructions } from "./instructions.js";
|
|
8
|
+
export { createConfiguredModelProvider, modelProviderAccountKey, modelProviderRecoveryKey, supportsModelProvider, } from "./providers/configured.js";
|
|
9
|
+
export { createOpenAICompatibleProvider, } from "./providers/openai-compatible.js";
|
|
10
|
+
export { checkReadiness } from "./readiness/index.js";
|
|
11
|
+
export { recoverRuns, runProject } from "./run/index.js";
|
|
12
|
+
export { normalizeScript, ScriptValidationError } from "./script.js";
|
|
13
|
+
export { createSqliteEventStore } from "./storage/index.js";
|
|
14
|
+
export { matchesMetadata } from "./storage/metadata.js";
|
|
15
|
+
export { createSqliteThreadStore, ThreadBusyError, } from "./storage/sqlite/threads.js";
|
|
16
|
+
export { createThreadTitler } from "./threads/titler.js";
|
|
17
|
+
export { discoverTools } from "./tools/index.js";
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type LoadedProject, type Skill } from "@plico-ai/core";
|
|
2
|
+
import type { ComposedInstructions } from "./types/index.js";
|
|
3
|
+
/** The agent's instructions plus a list of skills. A skill's full text loads on demand. */
|
|
4
|
+
export declare function composeInstructions(project: LoadedProject, loaded?: Skill[]): Promise<ComposedInstructions>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { getProjectPaths, getProjectRelativePaths, parseAgentFile, readSkills, SKILL_TOOL_NAME, } from "@plico-ai/core";
|
|
3
|
+
/** The agent's instructions plus a list of skills. A skill's full text loads on demand. */
|
|
4
|
+
export async function composeInstructions(project, loaded) {
|
|
5
|
+
const projectPaths = getProjectPaths(project.root);
|
|
6
|
+
const sources = [getProjectRelativePaths().agentFile];
|
|
7
|
+
const parts = [parseAgentFile(await readFile(projectPaths.agentFile, "utf8")).body];
|
|
8
|
+
const skills = loaded ?? (await readSkills(project.root)).skills;
|
|
9
|
+
if (skills.length > 0) {
|
|
10
|
+
sources.push(...skills.map((skill) => skill.source));
|
|
11
|
+
parts.push([
|
|
12
|
+
"## Skills",
|
|
13
|
+
"",
|
|
14
|
+
`Call ${SKILL_TOOL_NAME} with a skill's name to read its full instructions before you use it.`,
|
|
15
|
+
"",
|
|
16
|
+
...skills.map((skill) => `- ${skill.name}: ${skill.description}`),
|
|
17
|
+
].join("\n"));
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
sources,
|
|
21
|
+
content: parts.map((part) => part.trimEnd()).join("\n\n"),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ProjectConfig } from "@plico-ai/contracts";
|
|
2
|
+
import type { DeclaredTool } from "../types/tools.js";
|
|
3
|
+
export type ApprovalDecision = "none" | "required";
|
|
4
|
+
export declare function decideApproval(tool: DeclaredTool, config: ProjectConfig): {
|
|
5
|
+
required: boolean;
|
|
6
|
+
reason?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function approvalTimeoutMs(config: ProjectConfig): number;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function decideApproval(tool, config) {
|
|
2
|
+
const policy = config.approvals;
|
|
3
|
+
const override = policy?.tools?.[tool.name];
|
|
4
|
+
let decision;
|
|
5
|
+
if (override === "required" || override === "none") {
|
|
6
|
+
decision = override;
|
|
7
|
+
}
|
|
8
|
+
else if (tool.approval.required) {
|
|
9
|
+
decision = "required";
|
|
10
|
+
}
|
|
11
|
+
else if (policy?.default === "required") {
|
|
12
|
+
decision = "required";
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
decision = "none";
|
|
16
|
+
}
|
|
17
|
+
// Config can escalate but never silently drop a tool-declared requirement.
|
|
18
|
+
if (tool.approval.required && decision === "none") {
|
|
19
|
+
decision = "required";
|
|
20
|
+
}
|
|
21
|
+
return tool.approval.reason
|
|
22
|
+
? { required: decision === "required", reason: tool.approval.reason }
|
|
23
|
+
: { required: decision === "required" };
|
|
24
|
+
}
|
|
25
|
+
export function approvalTimeoutMs(config) {
|
|
26
|
+
return config.approvals?.timeoutMs ?? 30 * 60 * 1000;
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type LanguageModel } from "ai";
|
|
2
|
+
import type { ModelProvider } from "../types/index.js";
|
|
3
|
+
export interface AiSdkProviderOptions {
|
|
4
|
+
/** The variable that holds the credential. Without one, the model is built with no key. */
|
|
5
|
+
apiKeyEnv?: string | undefined;
|
|
6
|
+
/** Builds the AI SDK model once the credential is known. */
|
|
7
|
+
createModel: (apiKey: string | undefined) => LanguageModel | Promise<LanguageModel>;
|
|
8
|
+
}
|
|
9
|
+
/** Wraps an AI SDK language model behind the Plico model port. Provider retries are off. */
|
|
10
|
+
export declare function createAiSdkModelProvider(options: AiSdkProviderOptions): ModelProvider;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { APICallError, dynamicTool, jsonSchema, streamText, } from "ai";
|
|
2
|
+
import { ModelProviderError } from "../errors.js";
|
|
3
|
+
function toMessages(request) {
|
|
4
|
+
return request.messages.map((message) => {
|
|
5
|
+
if (message.role === "system" || message.role === "user")
|
|
6
|
+
return { role: message.role, content: message.content };
|
|
7
|
+
if (message.role === "tool")
|
|
8
|
+
return {
|
|
9
|
+
role: "tool",
|
|
10
|
+
content: [
|
|
11
|
+
{
|
|
12
|
+
type: "tool-result",
|
|
13
|
+
toolCallId: message.toolCallId ?? "",
|
|
14
|
+
toolName: message.toolName ?? "",
|
|
15
|
+
output: { type: "text", value: message.content },
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
const metadata = message.providerMetadata;
|
|
20
|
+
const envelope = metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata : undefined;
|
|
21
|
+
const adapter = envelope?.plicoAdapter;
|
|
22
|
+
const reasoning = adapter &&
|
|
23
|
+
typeof adapter === "object" &&
|
|
24
|
+
!Array.isArray(adapter) &&
|
|
25
|
+
typeof adapter.reasoning === "string"
|
|
26
|
+
? adapter.reasoning
|
|
27
|
+
: undefined;
|
|
28
|
+
const providerOptions = (envelope?.sdk ?? metadata);
|
|
29
|
+
return {
|
|
30
|
+
role: "assistant",
|
|
31
|
+
...(providerOptions === undefined ? {} : { providerOptions }),
|
|
32
|
+
content: [
|
|
33
|
+
...(reasoning ? [{ type: "reasoning", text: reasoning }] : []),
|
|
34
|
+
...(message.content ? [{ type: "text", text: message.content }] : []),
|
|
35
|
+
...(message.toolCalls ?? []).map((call) => ({
|
|
36
|
+
type: "tool-call",
|
|
37
|
+
toolCallId: call.id ?? "",
|
|
38
|
+
toolName: call.name,
|
|
39
|
+
input: call.arguments,
|
|
40
|
+
...(call.providerMetadata === undefined
|
|
41
|
+
? {}
|
|
42
|
+
: {
|
|
43
|
+
providerOptions: call.providerMetadata,
|
|
44
|
+
}),
|
|
45
|
+
})),
|
|
46
|
+
],
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function toTools(request) {
|
|
51
|
+
return Object.fromEntries((request.tools ?? []).map((entry) => [
|
|
52
|
+
entry.name,
|
|
53
|
+
dynamicTool({ description: entry.description, inputSchema: jsonSchema(entry.inputSchema) }),
|
|
54
|
+
]));
|
|
55
|
+
}
|
|
56
|
+
/** Wraps an AI SDK language model behind the Plico model port. Provider retries are off. */
|
|
57
|
+
export function createAiSdkModelProvider(options) {
|
|
58
|
+
return {
|
|
59
|
+
async *stream(request, { signal }) {
|
|
60
|
+
try {
|
|
61
|
+
const apiKey = options.apiKeyEnv ? process.env[options.apiKeyEnv] : undefined;
|
|
62
|
+
if (options.apiKeyEnv && !apiKey)
|
|
63
|
+
throw new ModelProviderError("auth", `Missing model provider credential: set ${options.apiKeyEnv}.`);
|
|
64
|
+
// Setup problems (a missing adapter package, a broken provider module) are local and
|
|
65
|
+
// actionable, so their message is kept. They are not a network failure and not retried.
|
|
66
|
+
let model;
|
|
67
|
+
try {
|
|
68
|
+
model = await options.createModel(apiKey);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
throw new ModelProviderError("bad_request", error instanceof Error ? error.message : "Model provider setup failed.");
|
|
72
|
+
}
|
|
73
|
+
const response = streamText({
|
|
74
|
+
model,
|
|
75
|
+
messages: toMessages(request),
|
|
76
|
+
tools: toTools(request),
|
|
77
|
+
allowSystemInMessages: true,
|
|
78
|
+
maxRetries: 0,
|
|
79
|
+
streamRetries: 0,
|
|
80
|
+
abortSignal: signal,
|
|
81
|
+
onError: () => { },
|
|
82
|
+
});
|
|
83
|
+
let metadata;
|
|
84
|
+
let reasoning = "";
|
|
85
|
+
for await (const part of response.fullStream) {
|
|
86
|
+
if (part.type === "text-delta")
|
|
87
|
+
yield { type: "text", text: part.text };
|
|
88
|
+
else if (part.type === "reasoning-delta")
|
|
89
|
+
reasoning += part.text;
|
|
90
|
+
else if (part.type === "tool-call")
|
|
91
|
+
yield {
|
|
92
|
+
type: "tool.call",
|
|
93
|
+
call: {
|
|
94
|
+
id: part.toolCallId,
|
|
95
|
+
name: part.toolName,
|
|
96
|
+
arguments: part.input,
|
|
97
|
+
...(part.providerMetadata
|
|
98
|
+
? { providerMetadata: part.providerMetadata }
|
|
99
|
+
: {}),
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
else if (part.type === "finish-step")
|
|
103
|
+
metadata = part.providerMetadata;
|
|
104
|
+
else if (part.type === "error")
|
|
105
|
+
throw part.error;
|
|
106
|
+
else if (part.type === "finish") {
|
|
107
|
+
if (part.finishReason !== "stop" && part.finishReason !== "tool-calls")
|
|
108
|
+
throw new ModelProviderError("bad_request", `Model response did not complete (finish reason: ${part.finishReason}).`);
|
|
109
|
+
yield {
|
|
110
|
+
type: "finish",
|
|
111
|
+
usage: {
|
|
112
|
+
inputTokens: part.totalUsage.inputTokens ?? 0,
|
|
113
|
+
outputTokens: part.totalUsage.outputTokens ?? 0,
|
|
114
|
+
},
|
|
115
|
+
...(!reasoning && metadata === undefined
|
|
116
|
+
? {}
|
|
117
|
+
: {
|
|
118
|
+
providerMetadata: {
|
|
119
|
+
...(metadata === undefined ? {} : { sdk: metadata }),
|
|
120
|
+
...(reasoning ? { plicoAdapter: { reasoning } } : {}),
|
|
121
|
+
},
|
|
122
|
+
}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (signal.aborted)
|
|
129
|
+
throw new ModelProviderError("aborted", "Model request aborted.");
|
|
130
|
+
if (error instanceof ModelProviderError)
|
|
131
|
+
throw error;
|
|
132
|
+
if (APICallError.isInstance(error)) {
|
|
133
|
+
const status = error.statusCode;
|
|
134
|
+
if (status === 401 || status === 403)
|
|
135
|
+
throw new ModelProviderError("auth", "Model provider authentication failed. Check credentials.");
|
|
136
|
+
if (status === 429)
|
|
137
|
+
throw new ModelProviderError("rate_limit", "Model provider rate limit reached.");
|
|
138
|
+
if (status === 503 || (status !== undefined && status >= 500))
|
|
139
|
+
throw new ModelProviderError("overloaded", "Model provider is unavailable.");
|
|
140
|
+
if (status !== undefined && status >= 400 && isContextLengthError(error))
|
|
141
|
+
throw new ModelProviderError("context_length", "The conversation is too long for the model's context window. Start a new thread, or choose a model with a larger context.");
|
|
142
|
+
if (status !== undefined && status >= 400)
|
|
143
|
+
throw new ModelProviderError("bad_request", `Model provider rejected the request (HTTP ${status}).`);
|
|
144
|
+
}
|
|
145
|
+
throw new ModelProviderError("network", "Model provider request failed.");
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const CONTEXT_LENGTH = /context[_ ]length|context window|prompt is too long|too many tokens|exceeds? the maximum (number of )?tokens|input token count/i;
|
|
151
|
+
/** Providers word this differently, so match the code and message they return for an over-long input. */
|
|
152
|
+
function isContextLengthError(error) {
|
|
153
|
+
return CONTEXT_LENGTH.test(`${error.message} ${error.responseBody ?? ""}`);
|
|
154
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ProjectConfig } from "@plico-ai/contracts";
|
|
2
|
+
import type { ModelProvider } from "../types/index.js";
|
|
3
|
+
type ModelConfig = NonNullable<ProjectConfig["model"]>;
|
|
4
|
+
export declare function supportsModelProvider(provider: string): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* The model port for a project's configured model. A provider outside the native list comes from
|
|
7
|
+
* `providers/<name>.ts` and needs the project root; without it there is no adapter.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createConfiguredModelProvider(model: ModelConfig, projectRoot?: string): ModelProvider | undefined;
|
|
10
|
+
/** Hash the provider target and credential without storing either secret in the journal. */
|
|
11
|
+
export declare function modelProviderRecoveryKey(model: ModelConfig): string;
|
|
12
|
+
/**
|
|
13
|
+
* Hash only the account and credential. A queued run has not called a model yet, so changing the
|
|
14
|
+
* configured model must not strand its reservation.
|
|
15
|
+
*/
|
|
16
|
+
export declare function modelProviderAccountKey(model: ModelConfig): string;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { NATIVE_MODEL_PROVIDERS } from "@plico-ai/core";
|
|
3
|
+
import { createAiSdkModelProvider } from "./ai-sdk.js";
|
|
4
|
+
import { createOpenAICompatibleProvider } from "./openai-compatible.js";
|
|
5
|
+
import { createProjectModuleProvider } from "./project-module.js";
|
|
6
|
+
import { nativeProvider, resolveModelEndpoint } from "./registry.js";
|
|
7
|
+
export function supportsModelProvider(provider) {
|
|
8
|
+
return NATIVE_MODEL_PROVIDERS.includes(provider);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The model port for a project's configured model. A provider outside the native list comes from
|
|
12
|
+
* `providers/<name>.ts` and needs the project root; without it there is no adapter.
|
|
13
|
+
*/
|
|
14
|
+
export function createConfiguredModelProvider(model, projectRoot) {
|
|
15
|
+
const { apiKeyEnv, baseURL } = resolveModelEndpoint(model);
|
|
16
|
+
const native = nativeProvider(model.provider);
|
|
17
|
+
if (native)
|
|
18
|
+
return createAiSdkModelProvider({
|
|
19
|
+
apiKeyEnv,
|
|
20
|
+
createModel: async (apiKey) => (await native.create({
|
|
21
|
+
model: model.name,
|
|
22
|
+
apiKey: apiKey ?? "",
|
|
23
|
+
baseURL,
|
|
24
|
+
})),
|
|
25
|
+
});
|
|
26
|
+
if (model.provider === "openai-compatible")
|
|
27
|
+
return createOpenAICompatibleProvider({
|
|
28
|
+
model: model.name,
|
|
29
|
+
baseURL,
|
|
30
|
+
...(apiKeyEnv ? { apiKeyEnv } : {}),
|
|
31
|
+
});
|
|
32
|
+
if (!projectRoot)
|
|
33
|
+
return undefined;
|
|
34
|
+
return createProjectModuleProvider(projectRoot, model.provider, {
|
|
35
|
+
model: model.name,
|
|
36
|
+
apiKeyEnv,
|
|
37
|
+
...(model.baseURL ? { baseURL: model.baseURL } : {}),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function hashParts(parts) {
|
|
41
|
+
return createHash("sha256").update(JSON.stringify(parts)).digest("hex");
|
|
42
|
+
}
|
|
43
|
+
/** Hash the provider target and credential without storing either secret in the journal. */
|
|
44
|
+
export function modelProviderRecoveryKey(model) {
|
|
45
|
+
const { apiKeyEnv, baseURL } = resolveModelEndpoint(model);
|
|
46
|
+
return hashParts([
|
|
47
|
+
model.provider,
|
|
48
|
+
model.name,
|
|
49
|
+
baseURL,
|
|
50
|
+
(apiKeyEnv ? process.env[apiKeyEnv] : "") ?? "",
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Hash only the account and credential. A queued run has not called a model yet, so changing the
|
|
55
|
+
* configured model must not strand its reservation.
|
|
56
|
+
*/
|
|
57
|
+
export function modelProviderAccountKey(model) {
|
|
58
|
+
const { apiKeyEnv, baseURL } = resolveModelEndpoint(model);
|
|
59
|
+
return hashParts([model.provider, baseURL, (apiKeyEnv ? process.env[apiKeyEnv] : "") ?? ""]);
|
|
60
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ModelProvider } from "../types/index.js";
|
|
2
|
+
export interface OpenAICompatibleProviderOptions {
|
|
3
|
+
model: string;
|
|
4
|
+
apiKeyEnv?: string;
|
|
5
|
+
baseURL?: string;
|
|
6
|
+
fetch?: typeof globalThis.fetch;
|
|
7
|
+
}
|
|
8
|
+
export declare function createOpenAICompatibleProvider(options: OpenAICompatibleProviderOptions): ModelProvider;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
2
|
+
import { createAiSdkModelProvider } from "./ai-sdk.js";
|
|
3
|
+
export function createOpenAICompatibleProvider(options) {
|
|
4
|
+
return createAiSdkModelProvider({
|
|
5
|
+
apiKeyEnv: options.apiKeyEnv ?? "OPENAI_API_KEY",
|
|
6
|
+
createModel: (apiKey) => createOpenAICompatible({
|
|
7
|
+
name: "plico",
|
|
8
|
+
baseURL: options.baseURL ?? "https://api.openai.com/v1",
|
|
9
|
+
...(apiKey ? { apiKey } : {}),
|
|
10
|
+
includeUsage: true,
|
|
11
|
+
...(options.fetch ? { fetch: options.fetch } : {}),
|
|
12
|
+
})(options.model),
|
|
13
|
+
});
|
|
14
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ModelProvider } from "../types/index.js";
|
|
2
|
+
/** A provider the project supplies in `providers/<name>.ts`, run behind the Plico model port. */
|
|
3
|
+
export declare function createProjectModuleProvider(root: string, provider: string, input: {
|
|
4
|
+
model: string;
|
|
5
|
+
apiKeyEnv: string | undefined;
|
|
6
|
+
baseURL?: string;
|
|
7
|
+
}): ModelProvider;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
import { findProjectProviderFile } from "@plico-ai/core";
|
|
3
|
+
import { register } from "tsx/esm/api";
|
|
4
|
+
import { createAiSdkModelProvider } from "./ai-sdk.js";
|
|
5
|
+
let registered = false;
|
|
6
|
+
/** A provider the project supplies in `providers/<name>.ts`, run behind the Plico model port. */
|
|
7
|
+
export function createProjectModuleProvider(root, provider, input) {
|
|
8
|
+
return createAiSdkModelProvider({
|
|
9
|
+
apiKeyEnv: input.apiKeyEnv,
|
|
10
|
+
createModel: async (apiKey) => {
|
|
11
|
+
const file = await findProjectProviderFile(root, provider);
|
|
12
|
+
if (!file)
|
|
13
|
+
throw new Error(`Missing model provider module: providers/${provider}.ts`);
|
|
14
|
+
if (!registered) {
|
|
15
|
+
register();
|
|
16
|
+
registered = true;
|
|
17
|
+
}
|
|
18
|
+
const loaded = (await import(pathToFileURL(file).href));
|
|
19
|
+
if (typeof loaded.default !== "function")
|
|
20
|
+
throw new Error(`providers/${provider} must export a function by default.`);
|
|
21
|
+
const options = {
|
|
22
|
+
model: input.model,
|
|
23
|
+
...(apiKey ? { apiKey } : {}),
|
|
24
|
+
...(input.baseURL ? { baseURL: input.baseURL } : {}),
|
|
25
|
+
};
|
|
26
|
+
return (await loaded.default(options));
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ProjectConfig } from "@plico-ai/contracts";
|
|
2
|
+
type ModelConfig = NonNullable<ProjectConfig["model"]>;
|
|
3
|
+
interface NativeProvider {
|
|
4
|
+
apiKeyEnv: string;
|
|
5
|
+
/** The endpoint used when neither the config nor the environment sets one. */
|
|
6
|
+
baseURL: string;
|
|
7
|
+
/** Returns an AI SDK language model. Adapter packages may pin another `@ai-sdk/provider` copy, so it is typed loosely here. */
|
|
8
|
+
create: (input: {
|
|
9
|
+
model: string;
|
|
10
|
+
apiKey: string;
|
|
11
|
+
baseURL: string;
|
|
12
|
+
}) => Promise<unknown>;
|
|
13
|
+
}
|
|
14
|
+
export declare function nativeProvider(provider: string): NativeProvider | undefined;
|
|
15
|
+
/** The credential variable and endpoint a configured model uses, for the run and the recovery key. */
|
|
16
|
+
export declare function resolveModelEndpoint(model: ModelConfig): {
|
|
17
|
+
apiKeyEnv: string | undefined;
|
|
18
|
+
baseURL: string;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** The adapters are optional peer packages, so each loads only when its provider is used. */
|
|
2
|
+
async function load(packageName, importer) {
|
|
3
|
+
try {
|
|
4
|
+
return await importer();
|
|
5
|
+
}
|
|
6
|
+
catch {
|
|
7
|
+
throw new Error(`Install ${packageName} to use this model provider: pnpm add ${packageName}`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
const OPENAI_URL = "https://api.openai.com/v1";
|
|
11
|
+
const NATIVE = {
|
|
12
|
+
anthropic: {
|
|
13
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
14
|
+
baseURL: "https://api.anthropic.com/v1",
|
|
15
|
+
create: async ({ model, apiKey, baseURL }) => (await load("@ai-sdk/anthropic", () => import("@ai-sdk/anthropic"))).createAnthropic({
|
|
16
|
+
apiKey,
|
|
17
|
+
baseURL,
|
|
18
|
+
})(model),
|
|
19
|
+
},
|
|
20
|
+
google: {
|
|
21
|
+
apiKeyEnv: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
22
|
+
baseURL: "https://generativelanguage.googleapis.com/v1beta",
|
|
23
|
+
create: async ({ model, apiKey, baseURL }) => (await load("@ai-sdk/google", () => import("@ai-sdk/google"))).createGoogleGenerativeAI({
|
|
24
|
+
apiKey,
|
|
25
|
+
baseURL,
|
|
26
|
+
})(model),
|
|
27
|
+
},
|
|
28
|
+
openai: {
|
|
29
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
30
|
+
baseURL: OPENAI_URL,
|
|
31
|
+
// The chat endpoint is the one every OpenAI-compatible server also speaks.
|
|
32
|
+
create: async ({ model, apiKey, baseURL }) => (await load("@ai-sdk/openai", () => import("@ai-sdk/openai")))
|
|
33
|
+
.createOpenAI({ apiKey, baseURL })
|
|
34
|
+
.chat(model),
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
export function nativeProvider(provider) {
|
|
38
|
+
return NATIVE[provider];
|
|
39
|
+
}
|
|
40
|
+
/** The credential variable and endpoint a configured model uses, for the run and the recovery key. */
|
|
41
|
+
export function resolveModelEndpoint(model) {
|
|
42
|
+
const native = nativeProvider(model.provider);
|
|
43
|
+
const usesOpenAIEnv = model.provider === "openai" || model.provider === "openai-compatible";
|
|
44
|
+
return {
|
|
45
|
+
apiKeyEnv: model.apiKeyEnv ??
|
|
46
|
+
native?.apiKeyEnv ??
|
|
47
|
+
(model.provider === "openai-compatible" ? "OPENAI_API_KEY" : undefined),
|
|
48
|
+
baseURL: model.baseURL ??
|
|
49
|
+
(usesOpenAIEnv ? process.env.OPENAI_BASE_URL : undefined) ??
|
|
50
|
+
native?.baseURL ??
|
|
51
|
+
OPENAI_URL,
|
|
52
|
+
};
|
|
53
|
+
}
|