mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-22 02:27:44 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
+255
@@ -0,0 +1,255 @@
|
||||
import { performance } from 'node:perf_hooks';
|
||||
|
||||
/** One-shot model-provider credential connectivity executor. */
|
||||
import {
|
||||
createModelProviderCredentialTestResult,
|
||||
type ModelProviderCredentialTestAllowlist,
|
||||
type ModelProviderCredentialTestExecution,
|
||||
type ModelProviderCredentialTestPlan,
|
||||
type ModelProviderCredentialTestResult,
|
||||
} from '@qinglong/ai/model-provider-credential-test-connection';
|
||||
import {
|
||||
ModelProviderCredentialTestExecutionUnavailableError,
|
||||
type BeginModelProviderCredentialTestExecutionInput,
|
||||
type ModelProviderCredentialTestExecutionRepository,
|
||||
} from '@qinglong/ai/postgres-model-provider-credential-test-connection';
|
||||
import { OpenAiCompatibleProvider } from '@qinglong/ai/openai-compatible';
|
||||
import {
|
||||
BoundModelProviderCredentialProvider,
|
||||
type ModelProviderCredentialAuditSink,
|
||||
type ModelProviderCredentialBindingSource,
|
||||
type ModelProviderSecretMaterialProvider,
|
||||
} from '@qinglong/ai/provider-credential';
|
||||
|
||||
export interface ExecuteModelProviderCredentialTestInput
|
||||
extends BeginModelProviderCredentialTestExecutionInput {}
|
||||
|
||||
interface ModelProviderCredentialTestExecutorEvidence {
|
||||
readonly plan: Readonly<ModelProviderCredentialTestPlan>;
|
||||
readonly execution: Readonly<ModelProviderCredentialTestExecution>;
|
||||
}
|
||||
|
||||
export type ExecuteModelProviderCredentialTestResult =
|
||||
| Readonly<
|
||||
ModelProviderCredentialTestExecutorEvidence & {
|
||||
status: 'completed' | 'existing';
|
||||
result: Readonly<ModelProviderCredentialTestResult>;
|
||||
}
|
||||
>
|
||||
| Readonly<
|
||||
ModelProviderCredentialTestExecutorEvidence & {
|
||||
status: 'outcome_unknown';
|
||||
result: null;
|
||||
}
|
||||
>;
|
||||
|
||||
export interface ModelProviderCredentialTestExecutor {
|
||||
execute(
|
||||
input: Readonly<ExecuteModelProviderCredentialTestInput>,
|
||||
): Promise<Readonly<ExecuteModelProviderCredentialTestResult>>;
|
||||
}
|
||||
|
||||
export interface ModelProviderCredentialTestExecutorOptions {
|
||||
readonly repository: ModelProviderCredentialTestExecutionRepository;
|
||||
readonly credentials: ModelProviderCredentialBindingSource &
|
||||
ModelProviderCredentialAuditSink;
|
||||
readonly secrets: ModelProviderSecretMaterialProvider;
|
||||
readonly fetch?: typeof globalThis.fetch;
|
||||
readonly now?: () => number;
|
||||
readonly monotonicNow?: () => number;
|
||||
readonly transportReady?: (
|
||||
baseUrl: string,
|
||||
signal: AbortSignal,
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
export class ModelProviderCredentialTestExecutorConfigurationError extends TypeError {
|
||||
readonly code =
|
||||
'MODEL_PROVIDER_CREDENTIAL_TEST_EXECUTOR_CONFIGURATION_INVALID';
|
||||
|
||||
constructor() {
|
||||
super('Model provider credential test executor configuration is invalid');
|
||||
this.name = 'ModelProviderCredentialTestExecutorConfigurationError';
|
||||
}
|
||||
}
|
||||
|
||||
export class ModelProviderCredentialTestExecutorUnavailableError extends Error {
|
||||
readonly code = 'MODEL_PROVIDER_CREDENTIAL_TEST_EXECUTOR_UNAVAILABLE';
|
||||
|
||||
constructor(options?: ErrorOptions) {
|
||||
super('Model provider credential test executor is unavailable', options);
|
||||
this.name = 'ModelProviderCredentialTestExecutorUnavailableError';
|
||||
}
|
||||
}
|
||||
|
||||
function exact(value: unknown, keys: readonly string[]): boolean {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
||||
const actual = Object.keys(value).sort();
|
||||
const expected = [...keys].sort();
|
||||
return (
|
||||
actual.length === expected.length &&
|
||||
actual.every((key, index) => key === expected[index])
|
||||
);
|
||||
}
|
||||
|
||||
function currentTime(now: () => number): number {
|
||||
const value = now();
|
||||
if (!Number.isSafeInteger(value) || value < 0) {
|
||||
throw new ModelProviderCredentialTestExecutorUnavailableError();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function elapsedMs(startedAt: number, monotonicNow: () => number): number {
|
||||
const value = monotonicNow() - startedAt;
|
||||
if (!Number.isFinite(value) || value < 0) {
|
||||
throw new ModelProviderCredentialTestExecutorUnavailableError();
|
||||
}
|
||||
return Math.floor(value);
|
||||
}
|
||||
|
||||
async function completeExactly(
|
||||
repository: ModelProviderCredentialTestExecutionRepository,
|
||||
result: Readonly<ModelProviderCredentialTestResult>,
|
||||
): Promise<void> {
|
||||
try {
|
||||
await repository.complete(result);
|
||||
} catch (error) {
|
||||
if (
|
||||
!(error instanceof ModelProviderCredentialTestExecutionUnavailableError)
|
||||
) {
|
||||
throw error;
|
||||
}
|
||||
await repository.complete(result);
|
||||
}
|
||||
}
|
||||
|
||||
export function createModelProviderCredentialTestExecutor(
|
||||
options: ModelProviderCredentialTestExecutorOptions,
|
||||
): Readonly<ModelProviderCredentialTestExecutor> {
|
||||
const expectedKeys = ['credentials', 'repository', 'secrets'];
|
||||
if (options?.fetch !== undefined) expectedKeys.push('fetch');
|
||||
if (options?.monotonicNow !== undefined) expectedKeys.push('monotonicNow');
|
||||
if (options?.now !== undefined) expectedKeys.push('now');
|
||||
if (options?.transportReady !== undefined)
|
||||
expectedKeys.push('transportReady');
|
||||
if (
|
||||
!exact(options, expectedKeys) ||
|
||||
typeof options.repository?.beginExecution !== 'function' ||
|
||||
typeof options.repository?.complete !== 'function' ||
|
||||
typeof options.credentials?.resolveModelProviderCredentialBinding !==
|
||||
'function' ||
|
||||
typeof options.credentials?.record !== 'function' ||
|
||||
typeof options.secrets?.resolveProjectSecretMaterial !== 'function' ||
|
||||
(options.fetch !== undefined && typeof options.fetch !== 'function') ||
|
||||
(options.now !== undefined && typeof options.now !== 'function') ||
|
||||
(options.monotonicNow !== undefined &&
|
||||
typeof options.monotonicNow !== 'function') ||
|
||||
(options.transportReady !== undefined &&
|
||||
typeof options.transportReady !== 'function')
|
||||
) {
|
||||
throw new ModelProviderCredentialTestExecutorConfigurationError();
|
||||
}
|
||||
const now = options.now ?? Date.now;
|
||||
const monotonicNow = options.monotonicNow ?? (() => performance.now());
|
||||
const credentials = new BoundModelProviderCredentialProvider({
|
||||
bindings: options.credentials,
|
||||
secrets: options.secrets,
|
||||
audit: options.credentials,
|
||||
now,
|
||||
});
|
||||
|
||||
return Object.freeze({
|
||||
async execute(input: Readonly<ExecuteModelProviderCredentialTestInput>) {
|
||||
if (!exact(input, ['allowlist', 'executionId', 'testId'])) {
|
||||
throw new ModelProviderCredentialTestExecutorConfigurationError();
|
||||
}
|
||||
const begun = await options.repository.beginExecution(input);
|
||||
if (begun.status === 'existing') {
|
||||
return begun.result === null
|
||||
? Object.freeze({
|
||||
status: 'outcome_unknown' as const,
|
||||
plan: begun.plan,
|
||||
execution: begun.execution,
|
||||
result: null,
|
||||
})
|
||||
: Object.freeze({
|
||||
status: 'existing' as const,
|
||||
plan: begun.plan,
|
||||
execution: begun.execution,
|
||||
result: begun.result,
|
||||
});
|
||||
}
|
||||
|
||||
const startedAt = monotonicNow();
|
||||
if (!Number.isFinite(startedAt) || startedAt < 0) {
|
||||
throw new ModelProviderCredentialTestExecutorUnavailableError();
|
||||
}
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(
|
||||
() => controller.abort(),
|
||||
begun.plan.endpoint.deadlineMs,
|
||||
);
|
||||
timeout.unref?.();
|
||||
let outcome: 'reachable' | 'unreachable' = 'unreachable';
|
||||
let modelCount: number | null = null;
|
||||
try {
|
||||
await options.transportReady?.(
|
||||
begun.plan.endpoint.baseUrl,
|
||||
controller.signal,
|
||||
);
|
||||
const provider = new OpenAiCompatibleProvider({
|
||||
type: begun.plan.provider,
|
||||
baseUrl: begun.plan.endpoint.baseUrl,
|
||||
credentials,
|
||||
maxResponseBytes: begun.plan.endpoint.maxResponseBytes,
|
||||
...(options.fetch === undefined ? {} : { fetch: options.fetch }),
|
||||
});
|
||||
const models = await provider.listModels({
|
||||
projectId: begun.plan.projectId,
|
||||
requestId: begun.execution.executionId,
|
||||
signal: controller.signal,
|
||||
});
|
||||
if (
|
||||
!controller.signal.aborted &&
|
||||
models.length <= begun.plan.endpoint.maxModels &&
|
||||
elapsedMs(startedAt, monotonicNow) <= begun.plan.endpoint.deadlineMs
|
||||
) {
|
||||
outcome = 'reachable';
|
||||
modelCount = models.length;
|
||||
}
|
||||
} catch {
|
||||
outcome = 'unreachable';
|
||||
modelCount = null;
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
const durationMs = Math.min(
|
||||
begun.plan.endpoint.deadlineMs,
|
||||
elapsedMs(startedAt, monotonicNow),
|
||||
);
|
||||
const result = createModelProviderCredentialTestResult({
|
||||
executionId: begun.execution.executionId,
|
||||
testId: begun.plan.testId,
|
||||
planDigest: begun.plan.planDigest,
|
||||
outcome,
|
||||
modelCount,
|
||||
durationMs,
|
||||
completedAtMs: currentTime(now),
|
||||
});
|
||||
try {
|
||||
await completeExactly(options.repository, result);
|
||||
} catch (error) {
|
||||
throw new ModelProviderCredentialTestExecutorUnavailableError({
|
||||
cause: error instanceof Error ? error : undefined,
|
||||
});
|
||||
}
|
||||
return Object.freeze({
|
||||
status: 'completed' as const,
|
||||
plan: begun.plan,
|
||||
execution: begun.execution,
|
||||
result,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user