@modelprofile.com/flexharness 10.13.0 → 10.15.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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.codesandbox.d.ts +39 -0
- package/dist_ts/classes.codesandbox.js +539 -0
- package/dist_ts/classes.flexharness.d.ts +10 -0
- package/dist_ts/classes.flexharness.js +148 -16
- package/dist_ts/classes.toolcatalog.d.ts +27 -7
- package/dist_ts/classes.toolcatalog.js +311 -69
- package/dist_ts/interfaces.d.ts +137 -5
- package/dist_ts/interfaces.js +32 -1
- package/dist_ts/plugins.d.ts +62 -1
- package/dist_ts/plugins.js +15 -1
- package/dist_ts/utils.harnessoptions.d.ts +9 -2
- package/dist_ts/utils.harnessoptions.js +52 -11
- package/dist_ts/utils.json.js +3 -1
- package/dist_ts/utils.tooloutput.d.ts +2 -0
- package/dist_ts/utils.tooloutput.js +11 -1
- package/dist_ts_compaction/plugins.d.ts +3 -3
- package/dist_ts_compaction/plugins.js +3 -3
- package/dist_ts_compaction/smartagent.compaction.d.ts +10 -1
- package/dist_ts_compaction/smartagent.compaction.js +79 -38
- package/package.json +16 -4
- package/readme.md +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.codesandbox.ts +565 -0
- package/ts/classes.flexharness.ts +152 -15
- package/ts/classes.toolcatalog.ts +344 -66
- package/ts/interfaces.ts +169 -5
- package/ts/plugins.ts +79 -0
- package/ts/readme.md +1 -1
- package/ts/utils.harnessoptions.ts +68 -12
- package/ts/utils.json.ts +2 -0
- package/ts/utils.tooloutput.ts +9 -0
- package/ts_compaction/plugins.ts +3 -3
- package/ts_compaction/smartagent.compaction.ts +90 -34
package/ts/interfaces.ts
CHANGED
|
@@ -31,6 +31,8 @@ export type TFlexRetryReason = plugins.TAgentRetryReason;
|
|
|
31
31
|
* Every call carries `provider` and `requestedModelId`; key usage caps on those two.
|
|
32
32
|
*/
|
|
33
33
|
export type TFlexModelCallUsage = plugins.TAgentModelCallUsage;
|
|
34
|
+
/** A model call's usage as harness events carry it: a reported call also states its cache hit ratio. */
|
|
35
|
+
export type TFlexObservedModelCallUsage = plugins.TAgentObservedModelCallUsage;
|
|
34
36
|
|
|
35
37
|
export interface IFlexTextPromptPart {
|
|
36
38
|
type: 'text';
|
|
@@ -135,6 +137,8 @@ export interface IFlexToolMessagePart {
|
|
|
135
137
|
output?: TJsonValue;
|
|
136
138
|
error?: string;
|
|
137
139
|
childSessionId?: string;
|
|
140
|
+
/** For a catalog call a `run_code` program made: the `run_code` call's ID. */
|
|
141
|
+
parentToolCallId?: string;
|
|
138
142
|
model?: IFlexModelIdentity;
|
|
139
143
|
/** ISO time the tool call began. Absent on parts recorded before part times existed. */
|
|
140
144
|
startedAt?: string;
|
|
@@ -683,6 +687,9 @@ export const FLEX_TOOL_CATALOG_LIMITS = Object.freeze({
|
|
|
683
687
|
maxDeclarationBytes: 16 * 1024,
|
|
684
688
|
maxTags: 16,
|
|
685
689
|
maxTagCharacters: 48,
|
|
690
|
+
maxAliases: 16,
|
|
691
|
+
maxAliasCharacters: 64,
|
|
692
|
+
maxVocabularyWords: 1024,
|
|
686
693
|
maxExamples: 4,
|
|
687
694
|
maxExampleBytes: 1024,
|
|
688
695
|
maxSchemaBytes: 64 * 1024,
|
|
@@ -693,8 +700,16 @@ export const FLEX_TOOL_CATALOG_LIMITS = Object.freeze({
|
|
|
693
700
|
maxDescribeTypes: 32,
|
|
694
701
|
maxDescribeBytes: 64 * 1024,
|
|
695
702
|
maxParamIssues: 10,
|
|
703
|
+
maxOutputTypeDepth: 32,
|
|
696
704
|
});
|
|
697
705
|
|
|
706
|
+
/**
|
|
707
|
+
* How many levels of named types a TypeScript declaration declares for a method's result by
|
|
708
|
+
* default: none. The result names its types, and `describe_tools({ types })` declares them on
|
|
709
|
+
* request, as a model needs them only to read fields of a result it has not seen.
|
|
710
|
+
*/
|
|
711
|
+
export const FLEX_TOOL_CATALOG_DEFAULT_OUTPUT_TYPE_DEPTH = 0;
|
|
712
|
+
|
|
698
713
|
export interface IFlexCatalogSearchInput {
|
|
699
714
|
/** Words to rank methods by. */
|
|
700
715
|
query?: string;
|
|
@@ -743,6 +758,11 @@ export type TFlexCatalogCallError =
|
|
|
743
758
|
more?: number;
|
|
744
759
|
/** The lookup that returns the method's declaration. */
|
|
745
760
|
see: string;
|
|
761
|
+
}
|
|
762
|
+
| {
|
|
763
|
+
error: 'denied';
|
|
764
|
+
method: string;
|
|
765
|
+
reason: string;
|
|
746
766
|
};
|
|
747
767
|
|
|
748
768
|
export interface IFlexCatalogToolDefinition {
|
|
@@ -761,7 +781,10 @@ export type TFlexCatalogSchema = { readonly [keyword: string]: unknown } | plugi
|
|
|
761
781
|
export interface IFlexCatalogRunContext<TScope = unknown> {
|
|
762
782
|
/** The dotted path the model called. */
|
|
763
783
|
readonly method: string;
|
|
764
|
-
/**
|
|
784
|
+
/**
|
|
785
|
+
* The call's ID, which its tool part and permission requests carry: the `execute` call's, or for
|
|
786
|
+
* a call inside `run_code` the inner call's, `<run_code call ID>.<n>`.
|
|
787
|
+
*/
|
|
765
788
|
readonly toolCallId: string;
|
|
766
789
|
readonly abortSignal: AbortSignal;
|
|
767
790
|
/** The run's tool-provider context: scope, session, run and `requestPermission`. */
|
|
@@ -776,10 +799,16 @@ export interface IFlexCatalogMethod<TScope = unknown, TParams = any, TResult = u
|
|
|
776
799
|
method: string;
|
|
777
800
|
/** One line of at most 120 characters, shown in search results. */
|
|
778
801
|
summary: string;
|
|
779
|
-
/** The full description, shown with the declaration. */
|
|
802
|
+
/** The full description, shown with the declaration. An empty or blank one counts as absent. */
|
|
780
803
|
description?: string;
|
|
781
804
|
/** More words `search_tools` matches. */
|
|
782
805
|
tags?: readonly string[];
|
|
806
|
+
/**
|
|
807
|
+
* Other names of the method, such as the one its users' language or a former API gives it:
|
|
808
|
+
* `search_tools` weighs them like the method's name, and ranks the method first for a query that
|
|
809
|
+
* names one.
|
|
810
|
+
*/
|
|
811
|
+
aliases?: readonly string[];
|
|
783
812
|
/** Code shown as `@example` in the declaration. */
|
|
784
813
|
examples?: readonly string[];
|
|
785
814
|
/** The parameter object's schema; `execute` validates every call against it. */
|
|
@@ -797,22 +826,72 @@ export interface IFlexCatalogMethod<TScope = unknown, TParams = any, TResult = u
|
|
|
797
826
|
params: TParams,
|
|
798
827
|
context: IFlexCatalogRunContext<TScope>,
|
|
799
828
|
): TResult | PromiseLike<TResult> | AsyncIterable<TResult>;
|
|
829
|
+
/**
|
|
830
|
+
* Decides each validated call before it runs: `run` (the default without this hook), `propose`
|
|
831
|
+
* to hand it to the catalog's `onProposals` instead of running it, or `{ deny }` to refuse it.
|
|
832
|
+
*/
|
|
833
|
+
onCall?(
|
|
834
|
+
params: TParams,
|
|
835
|
+
context: IFlexCatalogRunContext<TScope>,
|
|
836
|
+
): TFlexCatalogCallDecision | PromiseLike<TFlexCatalogCallDecision>;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
export type TFlexCatalogCallDecision = 'run' | 'propose' | { deny: string };
|
|
840
|
+
|
|
841
|
+
/** A call a method's `onCall` decided to propose instead of running. */
|
|
842
|
+
export interface IFlexCatalogProposal {
|
|
843
|
+
/** The call's ID: the `execute` call's, or the inner call's inside `run_code`. */
|
|
844
|
+
id: string;
|
|
845
|
+
method: string;
|
|
846
|
+
params: unknown;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
export interface IFlexCatalogProposalContext<TScope = unknown> {
|
|
850
|
+
/** The `execute` or `run_code` call that made the proposals. */
|
|
851
|
+
readonly toolCallId: string;
|
|
852
|
+
readonly abortSignal: AbortSignal;
|
|
853
|
+
readonly provider: Readonly<IFlexToolProviderContext<TScope>>;
|
|
800
854
|
}
|
|
801
855
|
|
|
802
856
|
/** A named type that method declarations refer to; it needs a `declaration`, a `schema`, or both. */
|
|
803
857
|
export interface IFlexCatalogType {
|
|
804
|
-
/**
|
|
858
|
+
/**
|
|
859
|
+
* The type's name, such as `IMoney`, which `$ref`s to `#/$defs/<name>` use. A name that is no
|
|
860
|
+
* JavaScript identifier, such as `IEvent_order.created` as schema generators name instances of
|
|
861
|
+
* generics, is declared under an identifier made of it: each other character becomes `_`, and a
|
|
862
|
+
* reserved word or a leading digit is escaped with `_` (`IEvent_order_created`). Its declaration
|
|
863
|
+
* names the original, and `describe_tools` takes either.
|
|
864
|
+
*/
|
|
805
865
|
name: string;
|
|
866
|
+
/** An empty or blank description counts as absent. */
|
|
806
867
|
description?: string;
|
|
807
868
|
/** Validates parameters that refer to this type, and generates its declaration. */
|
|
808
869
|
schema?: TFlexCatalogSchema;
|
|
809
|
-
/**
|
|
870
|
+
/**
|
|
871
|
+
* Declaration text that replaces the generated one, such as `interface IMoney { … }`. It declares
|
|
872
|
+
* the identifier the type is declared under.
|
|
873
|
+
*/
|
|
810
874
|
declaration?: string;
|
|
811
875
|
}
|
|
812
876
|
|
|
813
877
|
export interface IFlexToolCatalog<TScope = unknown> {
|
|
814
878
|
methods: readonly IFlexCatalogMethod<TScope>[];
|
|
815
879
|
types?: readonly IFlexCatalogType[];
|
|
880
|
+
/**
|
|
881
|
+
* The words the catalog's users say for its own words, by the catalog's word:
|
|
882
|
+
* `{ voucher: ['Beleg', 'Belege'], 'gross amount': ['Bruttobetrag'] }`. A `search_tools` query that
|
|
883
|
+
* says every word of an alias searches for the catalog's words as well.
|
|
884
|
+
*/
|
|
885
|
+
vocabulary?: Readonly<Record<string, readonly string[]>>;
|
|
886
|
+
/**
|
|
887
|
+
* Receives the calls `onCall` proposed, as one batch: the proposed call of an `execute`, or every
|
|
888
|
+
* call a `run_code` program proposed, once the program finished. A failed program proposes
|
|
889
|
+
* nothing. What it returns is what the model sees for them.
|
|
890
|
+
*/
|
|
891
|
+
onProposals?(
|
|
892
|
+
proposals: readonly IFlexCatalogProposal[],
|
|
893
|
+
context: IFlexCatalogProposalContext<TScope>,
|
|
894
|
+
): unknown;
|
|
816
895
|
}
|
|
817
896
|
|
|
818
897
|
export interface IFlexToolCatalogOptions {
|
|
@@ -821,8 +900,93 @@ export interface IFlexToolCatalogOptions {
|
|
|
821
900
|
* across the session, or `json-schema` with each method's self-contained JSON Schema.
|
|
822
901
|
*/
|
|
823
902
|
declarations?: 'typescript' | 'json-schema';
|
|
903
|
+
/**
|
|
904
|
+
* How many levels of named types a TypeScript declaration declares for a method's result:
|
|
905
|
+
* `0` (the default) none, `1` the types the result names, each further level the types those
|
|
906
|
+
* name. A parameter's types are always declared in full, since a call has to match them. The
|
|
907
|
+
* declaration lists the types it names but does not declare; `describe_tools({ types })`
|
|
908
|
+
* declares them on request.
|
|
909
|
+
*/
|
|
910
|
+
outputTypeDepth?: number;
|
|
911
|
+
/**
|
|
912
|
+
* `search_tools` follows its results with the declaration of the top match when one match
|
|
913
|
+
* clearly leads, so the model can call it without a `describe_tools` step: the only match, or one
|
|
914
|
+
* whose name or alias the query names that ranks 1.25 times higher than the next. Default `true`.
|
|
915
|
+
*/
|
|
916
|
+
describeTopMatch?: boolean;
|
|
917
|
+
/**
|
|
918
|
+
* Adds `run_code`, which runs a short TypeScript or JavaScript program against the catalog in a
|
|
919
|
+
* QuickJS sandbox. Needs the optional peers `quickjs-emscripten-core`,
|
|
920
|
+
* `@jitl/quickjs-ng-wasmfile-release-sync` and `amaro`.
|
|
921
|
+
*/
|
|
922
|
+
codeMode?: boolean | IFlexCodeModeOptions;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
export interface IFlexCodeModeOptions {
|
|
926
|
+
limits?: Partial<IFlexCodeModeLimits>;
|
|
824
927
|
}
|
|
825
928
|
|
|
929
|
+
/** The bounds of one `run_code` program. */
|
|
930
|
+
export interface IFlexCodeModeLimits {
|
|
931
|
+
/** UTF-8 bytes of the program. */
|
|
932
|
+
maxSourceBytes: number;
|
|
933
|
+
/** Milliseconds the program may compute; time waiting for calls does not count. */
|
|
934
|
+
cpuMs: number;
|
|
935
|
+
/** Milliseconds the program may take in all, not counting waits for a permission decision. */
|
|
936
|
+
wallMs: number;
|
|
937
|
+
/** Bytes of the sandbox's heap. */
|
|
938
|
+
memoryBytes: number;
|
|
939
|
+
/**
|
|
940
|
+
* Bytes of the sandbox's stack. QuickJS runs on the host's stack as well, so a larger value lets
|
|
941
|
+
* deep recursion reach the host's limit first; the program then fails at its `stack` limit all
|
|
942
|
+
* the same.
|
|
943
|
+
*/
|
|
944
|
+
stackBytes: number;
|
|
945
|
+
/** Catalog calls per program. */
|
|
946
|
+
maxCalls: number;
|
|
947
|
+
/** Catalog calls running at once; further calls wait for a slot. */
|
|
948
|
+
maxConcurrentCalls: number;
|
|
949
|
+
/** UTF-8 bytes of console output kept; more is cut off. */
|
|
950
|
+
maxLogBytes: number;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
export const FLEX_CODE_MODE_TOOL_NAME = 'run_code';
|
|
954
|
+
|
|
955
|
+
export const FLEX_CODE_MODE_DEFAULT_LIMITS: Readonly<IFlexCodeModeLimits> = Object.freeze({
|
|
956
|
+
maxSourceBytes: 32 * 1024,
|
|
957
|
+
cpuMs: 5_000,
|
|
958
|
+
wallMs: 60_000,
|
|
959
|
+
memoryBytes: 64 * 1024 * 1024,
|
|
960
|
+
stackBytes: 64 * 1024,
|
|
961
|
+
maxCalls: 200,
|
|
962
|
+
maxConcurrentCalls: 8,
|
|
963
|
+
maxLogBytes: 16 * 1024,
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
export const FLEX_CODE_MODE_MAXIMUM_LIMITS: Readonly<IFlexCodeModeLimits> = Object.freeze({
|
|
967
|
+
maxSourceBytes: 256 * 1024,
|
|
968
|
+
cpuMs: 60_000,
|
|
969
|
+
wallMs: 30 * 60_000,
|
|
970
|
+
memoryBytes: 512 * 1024 * 1024,
|
|
971
|
+
stackBytes: 256 * 1024,
|
|
972
|
+
maxCalls: 10_000,
|
|
973
|
+
maxConcurrentCalls: 64,
|
|
974
|
+
maxLogBytes: 256 * 1024,
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
/** What `run_code` reports to the model, as JSON, when a program does not finish. */
|
|
978
|
+
export interface IFlexCodeRunFailure {
|
|
979
|
+
error: 'syntax_error' | 'program_error' | 'limit_exceeded' | 'aborted';
|
|
980
|
+
/** The limit that stopped the program, for `limit_exceeded`. */
|
|
981
|
+
limit?: 'source' | 'cpu' | 'wall' | 'memory' | 'stack' | 'calls';
|
|
982
|
+
name?: string;
|
|
983
|
+
message: string;
|
|
984
|
+
/** The program's line, when known. */
|
|
985
|
+
line?: number;
|
|
986
|
+
/** Catalog calls the program made. */
|
|
987
|
+
calls: number;
|
|
988
|
+
logs?: string;
|
|
989
|
+
}
|
|
826
990
|
export interface IFlexResourceToolProviderResolverContext<TScope> extends IFlexSessionGeneration {
|
|
827
991
|
scopeId: string;
|
|
828
992
|
scope: TScope;
|
|
@@ -1762,7 +1926,7 @@ export type TFlexModelUsageEvent = IFlexEventBase & {
|
|
|
1762
1926
|
readonly type: 'model.usage';
|
|
1763
1927
|
readonly runId?: string;
|
|
1764
1928
|
readonly source: 'generation' | 'compaction';
|
|
1765
|
-
} & Readonly<
|
|
1929
|
+
} & Readonly<TFlexObservedModelCallUsage>;
|
|
1766
1930
|
|
|
1767
1931
|
export interface IFlexPromptQueueEvent extends IFlexEventBase {
|
|
1768
1932
|
readonly type: 'prompt.queued' | 'prompt.started' | 'prompt.running' | 'prompt.finished';
|
package/ts/plugins.ts
CHANGED
|
@@ -13,6 +13,84 @@ import { Validator as JsonSchemaValidator } from '@cfworker/json-schema';
|
|
|
13
13
|
export { asSchema, jsonSchema, JsonSchemaValidator };
|
|
14
14
|
export type { FlexibleSchema, JSONSchema7, ModelMessage, Schema } from 'ai';
|
|
15
15
|
|
|
16
|
+
// optional code mode peers: QuickJS-ng compiled to WebAssembly, and amaro to strip TypeScript types
|
|
17
|
+
|
|
18
|
+
/** A value inside a QuickJS context; the host disposes every handle it owns. */
|
|
19
|
+
export interface IQuickJsHandle {
|
|
20
|
+
readonly alive: boolean;
|
|
21
|
+
dispose(): void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type TQuickJsResult =
|
|
25
|
+
| { value: IQuickJsHandle; error?: undefined }
|
|
26
|
+
| { error: IQuickJsHandle; value?: undefined };
|
|
27
|
+
|
|
28
|
+
export interface IQuickJsDeferredPromise {
|
|
29
|
+
readonly handle: IQuickJsHandle;
|
|
30
|
+
readonly alive: boolean;
|
|
31
|
+
resolve(value?: IQuickJsHandle): void;
|
|
32
|
+
reject(value?: IQuickJsHandle): void;
|
|
33
|
+
dispose(): void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface IQuickJsContext {
|
|
37
|
+
readonly global: IQuickJsHandle;
|
|
38
|
+
newString(value: string): IQuickJsHandle;
|
|
39
|
+
getString(handle: IQuickJsHandle): string;
|
|
40
|
+
newFunction(name: string, implementation: (...args: IQuickJsHandle[]) => IQuickJsHandle | void): IQuickJsHandle;
|
|
41
|
+
setProp(target: IQuickJsHandle, key: string, value: IQuickJsHandle): void;
|
|
42
|
+
newPromise(): IQuickJsDeferredPromise;
|
|
43
|
+
newError(message: string): IQuickJsHandle;
|
|
44
|
+
evalCode(code: string, filename?: string): TQuickJsResult;
|
|
45
|
+
resolvePromise(handle: IQuickJsHandle): Promise<TQuickJsResult>;
|
|
46
|
+
dump(handle: IQuickJsHandle): unknown;
|
|
47
|
+
dispose(): void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface IQuickJsRuntime {
|
|
51
|
+
readonly alive: boolean;
|
|
52
|
+
setMemoryLimit(bytes: number): void;
|
|
53
|
+
setMaxStackSize(bytes: number): void;
|
|
54
|
+
setInterruptHandler(handler: () => boolean): void;
|
|
55
|
+
newContext(): IQuickJsContext;
|
|
56
|
+
/** Runs the queued promise jobs; `error` is an exception none of them caught. */
|
|
57
|
+
executePendingJobs(): { error?: IQuickJsHandle };
|
|
58
|
+
dispose(): void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface IQuickJsModule {
|
|
62
|
+
newRuntime(): IQuickJsRuntime;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** A failed strip, as amaro reports it. */
|
|
66
|
+
export interface IAmaroError {
|
|
67
|
+
code?: string;
|
|
68
|
+
message?: string;
|
|
69
|
+
startLine?: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface ICodeSandboxEngine {
|
|
73
|
+
/** A new WebAssembly instance of QuickJS-ng, with a memory of its own. */
|
|
74
|
+
newModule(): Promise<IQuickJsModule>;
|
|
75
|
+
/** Removes TypeScript type syntax, keeping every line where it was. */
|
|
76
|
+
stripTypes(source: string): string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Loads QuickJS and amaro, the optional peers code mode needs. */
|
|
80
|
+
export const importCodeSandboxEngine = async (): Promise<ICodeSandboxEngine> => {
|
|
81
|
+
const [core, variant, amaro] = await Promise.all([
|
|
82
|
+
import('quickjs-emscripten-core'),
|
|
83
|
+
import('@jitl/quickjs-ng-wasmfile-release-sync'),
|
|
84
|
+
import('amaro'),
|
|
85
|
+
]);
|
|
86
|
+
// The variant's typings resolve as CommonJS; at run time the default export is the variant.
|
|
87
|
+
const quickjsVariant = (variant as { default: unknown }).default as Parameters<typeof core.newQuickJSWASMModuleFromVariant>[0];
|
|
88
|
+
return {
|
|
89
|
+
newModule: async () => await core.newQuickJSWASMModuleFromVariant(quickjsVariant) as unknown as IQuickJsModule,
|
|
90
|
+
stripTypes: (source) => amaro.transformSync(source, { mode: 'strip-only' }).code,
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
|
|
16
94
|
// @push.rocks scope
|
|
17
95
|
import {
|
|
18
96
|
AgentEventStoreConflictError,
|
|
@@ -80,6 +158,7 @@ export type {
|
|
|
80
158
|
TAgentGenerationOutcome,
|
|
81
159
|
TAgentModelCallUsage,
|
|
82
160
|
TAgentModelCallUsageEvent,
|
|
161
|
+
TAgentObservedModelCallUsage,
|
|
83
162
|
TAgentRetryReason,
|
|
84
163
|
TAgentToolCallFinishEvent,
|
|
85
164
|
TAgentToolExecutionReconciliationOptions,
|
package/ts/readme.md
CHANGED
|
@@ -1026,7 +1026,7 @@ A `contextCompactor` states which model to compact with from the model the compa
|
|
|
1026
1026
|
|
|
1027
1027
|
A compactor uses `resolvedModel.model` directly, and resolves `modelHint` the way its model resolver resolves a prompt's `modelHint`. Only when neither member is present, because nothing dispatched states a model, does the host choose the compaction model itself.
|
|
1028
1028
|
|
|
1029
|
-
A `context-overflow` or `context-budget` compaction also carries `system` and `providerOptions`: the instructions and the provider options (cache options applied, so `store: false` on OpenAI) the run's turns are sent with. `compactMessages()` sends its summary call with them as one streamed call, the shape of every turn, so a compactor that passes its options through
|
|
1029
|
+
A `context-overflow` or `context-budget` compaction also carries `system` and `providerOptions`: the instructions and the provider options (cache options applied, so `store: false` on OpenAI) the run's turns are sent with. A `context-budget` compaction on a provider that caches the prefix also carries the turns' `tools`. `compactMessages()` sends its summary call with them as one streamed call, the shape of every turn, with the tools' definitions but none of their code, so a compactor that passes its options through repeats the run's prefix and reads it from the provider's prompt cache; definitions that outweigh the history nine times are left out. Without `system` it sends its own instructions.
|
|
1030
1030
|
|
|
1031
1031
|
```typescript
|
|
1032
1032
|
agentSessionPolicy: {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { FlexHarnessValidationError } from './errors.js';
|
|
2
2
|
import {
|
|
3
|
+
FLEX_CODE_MODE_DEFAULT_LIMITS,
|
|
4
|
+
FLEX_CODE_MODE_MAXIMUM_LIMITS,
|
|
3
5
|
FLEX_REVERSION_DEFAULT_LIMITS,
|
|
6
|
+
FLEX_TOOL_CATALOG_DEFAULT_OUTPUT_TYPE_DEPTH,
|
|
7
|
+
FLEX_TOOL_CATALOG_LIMITS,
|
|
4
8
|
FLEX_REVERSION_MAXIMUM_LIMITS,
|
|
5
9
|
} from './interfaces.js';
|
|
6
10
|
import type {
|
|
@@ -12,6 +16,7 @@ import type {
|
|
|
12
16
|
IFlexReversionLimits,
|
|
13
17
|
IFlexSlashCommandHandlerRegistration,
|
|
14
18
|
IFlexSlashCommandTemplateRegistration,
|
|
19
|
+
IFlexCodeModeLimits,
|
|
15
20
|
IFlexSubagentDefinition,
|
|
16
21
|
IFlexToolCatalogOptions,
|
|
17
22
|
TFlexSlashCommandRegistration,
|
|
@@ -157,25 +162,76 @@ export function normalizeSubagents(
|
|
|
157
162
|
return Object.freeze(normalized);
|
|
158
163
|
}
|
|
159
164
|
|
|
165
|
+
export interface INormalizedToolCatalogOptions {
|
|
166
|
+
readonly declarations: 'typescript' | 'json-schema';
|
|
167
|
+
readonly outputTypeDepth: number;
|
|
168
|
+
readonly describeTopMatch: boolean;
|
|
169
|
+
/** The limits of `run_code`; absent while code mode is off. */
|
|
170
|
+
readonly codeMode?: Readonly<IFlexCodeModeLimits>;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const isPlainOptions = (value: unknown): value is Record<string, unknown> =>
|
|
174
|
+
!!value
|
|
175
|
+
&& typeof value === 'object'
|
|
176
|
+
&& !Array.isArray(value)
|
|
177
|
+
&& [Object.prototype, null].includes(Object.getPrototypeOf(value));
|
|
178
|
+
|
|
160
179
|
export function normalizeToolCatalogOptions(
|
|
161
|
-
options:
|
|
162
|
-
):
|
|
163
|
-
if (options === undefined)
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
) {
|
|
170
|
-
throw new FlexHarnessValidationError('toolCatalog must be a plain object.');
|
|
180
|
+
options: IFlexToolCatalogOptions | undefined,
|
|
181
|
+
): INormalizedToolCatalogOptions {
|
|
182
|
+
if (options === undefined) {
|
|
183
|
+
return Object.freeze({
|
|
184
|
+
declarations: 'typescript',
|
|
185
|
+
outputTypeDepth: FLEX_TOOL_CATALOG_DEFAULT_OUTPUT_TYPE_DEPTH,
|
|
186
|
+
describeTopMatch: true,
|
|
187
|
+
});
|
|
171
188
|
}
|
|
172
|
-
|
|
189
|
+
if (!isPlainOptions(options)) throw new FlexHarnessValidationError('toolCatalog must be a plain object.');
|
|
190
|
+
const unsupported = Object.keys(options).find(
|
|
191
|
+
(key) => !['declarations', 'outputTypeDepth', 'describeTopMatch', 'codeMode'].includes(key),
|
|
192
|
+
);
|
|
173
193
|
if (unsupported) throw new FlexHarnessValidationError(`toolCatalog does not support "${unsupported}".`);
|
|
174
194
|
const declarations = options.declarations ?? 'typescript';
|
|
175
195
|
if (declarations !== 'typescript' && declarations !== 'json-schema') {
|
|
176
196
|
throw new FlexHarnessValidationError('toolCatalog.declarations must be "typescript" or "json-schema".');
|
|
177
197
|
}
|
|
178
|
-
|
|
198
|
+
const outputTypeDepth: unknown = options.outputTypeDepth ?? FLEX_TOOL_CATALOG_DEFAULT_OUTPUT_TYPE_DEPTH;
|
|
199
|
+
if (
|
|
200
|
+
typeof outputTypeDepth !== 'number' || !Number.isSafeInteger(outputTypeDepth)
|
|
201
|
+
|| outputTypeDepth < 0 || outputTypeDepth > FLEX_TOOL_CATALOG_LIMITS.maxOutputTypeDepth
|
|
202
|
+
) {
|
|
203
|
+
throw new FlexHarnessValidationError(
|
|
204
|
+
`toolCatalog.outputTypeDepth must be an integer from 0 through ${FLEX_TOOL_CATALOG_LIMITS.maxOutputTypeDepth}.`,
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
const describeTopMatch = options.describeTopMatch ?? true;
|
|
208
|
+
if (typeof describeTopMatch !== 'boolean') throw new FlexHarnessValidationError('toolCatalog.describeTopMatch must be a boolean.');
|
|
209
|
+
const codeMode = normalizeCodeModeLimits(options.codeMode);
|
|
210
|
+
return Object.freeze({ declarations, outputTypeDepth, describeTopMatch, ...(codeMode ? { codeMode } : {}) });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function normalizeCodeModeLimits(codeMode: unknown): Readonly<IFlexCodeModeLimits> | undefined {
|
|
214
|
+
if (codeMode === undefined || codeMode === false) return undefined;
|
|
215
|
+
if (codeMode === true) return FLEX_CODE_MODE_DEFAULT_LIMITS;
|
|
216
|
+
if (!isPlainOptions(codeMode)) {
|
|
217
|
+
throw new FlexHarnessValidationError('toolCatalog.codeMode must be a boolean or a plain object.');
|
|
218
|
+
}
|
|
219
|
+
const unsupported = Object.keys(codeMode).find((key) => key !== 'limits');
|
|
220
|
+
if (unsupported) throw new FlexHarnessValidationError(`toolCatalog.codeMode does not support "${unsupported}".`);
|
|
221
|
+
const limits = codeMode.limits ?? {};
|
|
222
|
+
if (!isPlainOptions(limits)) throw new FlexHarnessValidationError('toolCatalog.codeMode.limits must be a plain object.');
|
|
223
|
+
const resolved: Record<string, number> = { ...FLEX_CODE_MODE_DEFAULT_LIMITS };
|
|
224
|
+
for (const [key, value] of Object.entries(limits)) {
|
|
225
|
+
if (!Object.prototype.hasOwnProperty.call(FLEX_CODE_MODE_MAXIMUM_LIMITS, key)) {
|
|
226
|
+
throw new FlexHarnessValidationError(`toolCatalog.codeMode.limits does not support "${key}".`);
|
|
227
|
+
}
|
|
228
|
+
const maximum = FLEX_CODE_MODE_MAXIMUM_LIMITS[key as keyof IFlexCodeModeLimits];
|
|
229
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 1 || value > maximum) {
|
|
230
|
+
throw new FlexHarnessValidationError(`toolCatalog.codeMode.limits.${key} must be an integer from 1 through ${maximum}.`);
|
|
231
|
+
}
|
|
232
|
+
resolved[key] = value;
|
|
233
|
+
}
|
|
234
|
+
return Object.freeze(resolved as unknown as IFlexCodeModeLimits);
|
|
179
235
|
}
|
|
180
236
|
|
|
181
237
|
export function normalizeBuiltInTools(
|
package/ts/utils.json.ts
CHANGED
|
@@ -785,6 +785,7 @@ function validateMessage(value: unknown, path: string): IValidatedMessageIdentit
|
|
|
785
785
|
'output',
|
|
786
786
|
'error',
|
|
787
787
|
'childSessionId',
|
|
788
|
+
'parentToolCallId',
|
|
788
789
|
'model',
|
|
789
790
|
'startedAt',
|
|
790
791
|
'completedAt',
|
|
@@ -801,6 +802,7 @@ function validateMessage(value: unknown, path: string): IValidatedMessageIdentit
|
|
|
801
802
|
}
|
|
802
803
|
requireOptionalString(part.error, `${partPath}.error`);
|
|
803
804
|
requireOptionalString(part.childSessionId, `${partPath}.childSessionId`);
|
|
805
|
+
requireOptionalString(part.parentToolCallId, `${partPath}.parentToolCallId`);
|
|
804
806
|
if (part.model !== undefined) validateModel(part.model, `${partPath}.model`);
|
|
805
807
|
requireOptionalString(part.startedAt, `${partPath}.startedAt`);
|
|
806
808
|
requireOptionalString(part.completedAt, `${partPath}.completedAt`);
|
package/ts/utils.tooloutput.ts
CHANGED
|
@@ -25,6 +25,15 @@ async function* normalizeAsyncIterable(
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/** A tool output's final value: the value itself, or the last value an async iterable yields. */
|
|
29
|
+
export async function settleToolOutput(output: unknown): Promise<unknown> {
|
|
30
|
+
const value = await output;
|
|
31
|
+
if (!isAsyncIterable(value)) return value;
|
|
32
|
+
let last: unknown;
|
|
33
|
+
for await (const item of value) last = item;
|
|
34
|
+
return last;
|
|
35
|
+
}
|
|
36
|
+
|
|
28
37
|
export function wrapToolSet(
|
|
29
38
|
tools: TFlexAgentToolSet,
|
|
30
39
|
limits: Required<NonNullable<IFlexHarnessOptions<unknown>['toolOutputLimits']>>,
|
package/ts_compaction/plugins.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { streamText, wrapLanguageModel } from 'ai';
|
|
1
|
+
import { asSchema, streamText, wrapLanguageModel } from 'ai';
|
|
2
2
|
|
|
3
|
-
export { streamText, wrapLanguageModel };
|
|
3
|
+
export { asSchema, streamText, wrapLanguageModel };
|
|
4
4
|
|
|
5
|
-
export type { ModelMessage } from 'ai';
|
|
5
|
+
export type { ModelMessage, ToolSet } from 'ai';
|
|
6
6
|
export type { TSmartAiLanguageModel } from '@modelprofile.com/flexharness-models';
|
|
7
7
|
|
|
8
8
|
import { AgentModelCallUsageRecorder } from '@modelprofile.com/flexharness-agent';
|