@futdevpro/nts-dynamo 1.15.105 → 1.15.110
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/.dynamo/logs/cicd-pipeline/output.log +1621 -1669
- package/.dynamo/logs/cicd-pipeline/status.json +43 -29
- package/build/_modules/ai/_collections/ai-model-config-validator.util.d.ts +32 -0
- package/build/_modules/ai/_collections/ai-model-config-validator.util.d.ts.map +1 -0
- package/build/_modules/ai/_collections/ai-model-config-validator.util.js +95 -0
- package/build/_modules/ai/_collections/ai-model-config-validator.util.js.map +1 -0
- package/build/_modules/ai/_collections/ai-provider-error.util.d.ts +44 -0
- package/build/_modules/ai/_collections/ai-provider-error.util.d.ts.map +1 -0
- package/build/_modules/ai/_collections/ai-provider-error.util.js +121 -0
- package/build/_modules/ai/_collections/ai-provider-error.util.js.map +1 -0
- package/build/_modules/ai/_models/interfaces/dynts-ai-provider-error.interface.d.ts +17 -0
- package/build/_modules/ai/_models/interfaces/dynts-ai-provider-error.interface.d.ts.map +1 -0
- package/build/_modules/ai/_models/interfaces/dynts-ai-provider-error.interface.js +3 -0
- package/build/_modules/ai/_models/interfaces/dynts-ai-provider-error.interface.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_collections/aim-default-collection.util.d.ts +23 -0
- package/build/_modules/ai/_modules/ai-mock/_collections/aim-default-collection.util.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_collections/aim-default-collection.util.js +52 -0
- package/build/_modules/ai/_modules/ai-mock/_collections/aim-default-collection.util.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_collections/aim-fixture-match.util.d.ts +34 -0
- package/build/_modules/ai/_modules/ai-mock/_collections/aim-fixture-match.util.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_collections/aim-fixture-match.util.js +56 -0
- package/build/_modules/ai/_modules/ai-mock/_collections/aim-fixture-match.util.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_collections/aim-fixture-serializer.util.d.ts +33 -0
- package/build/_modules/ai/_modules/ai-mock/_collections/aim-fixture-serializer.util.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_collections/aim-fixture-serializer.util.js +89 -0
- package/build/_modules/ai/_modules/ai-mock/_collections/aim-fixture-serializer.util.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_enums/aim-modality.enum.d.ts +16 -0
- package/build/_modules/ai/_modules/ai-mock/_enums/aim-modality.enum.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_enums/aim-modality.enum.js +20 -0
- package/build/_modules/ai/_modules/ai-mock/_enums/aim-modality.enum.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-call-input.interface.d.ts +15 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-call-input.interface.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-call-input.interface.js +3 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-call-input.interface.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-error.interface.d.ts +11 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-error.interface.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-error.interface.js +3 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-error.interface.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-fixture.interface.d.ts +30 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-fixture.interface.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-fixture.interface.js +3 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-fixture.interface.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-request.interface.d.ts +19 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-request.interface.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-request.interface.js +3 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-request.interface.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-service-collection.interface.d.ts +19 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-service-collection.interface.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-service-collection.interface.js +3 -0
- package/build/_modules/ai/_modules/ai-mock/_models/interfaces/aim-service-collection.interface.js.map +1 -0
- package/build/_modules/ai/{_services → _modules/ai-mock/_services}/ai-embedding-mock.service.d.ts +2 -2
- package/build/_modules/ai/_modules/ai-mock/_services/ai-embedding-mock.service.d.ts.map +1 -0
- package/build/_modules/ai/{_services → _modules/ai-mock/_services}/ai-embedding-mock.service.js +1 -1
- package/build/_modules/ai/_modules/ai-mock/_services/ai-embedding-mock.service.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_services/aim-fixture-harness.service.d.ts +48 -0
- package/build/_modules/ai/_modules/ai-mock/_services/aim-fixture-harness.service.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_services/aim-fixture-harness.service.js +104 -0
- package/build/_modules/ai/_modules/ai-mock/_services/aim-fixture-harness.service.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_services/aim-fixture-store.service.d.ts +35 -0
- package/build/_modules/ai/_modules/ai-mock/_services/aim-fixture-store.service.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_services/aim-fixture-store.service.js +60 -0
- package/build/_modules/ai/_modules/ai-mock/_services/aim-fixture-store.service.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_services/aim-mock.service.d.ts +60 -0
- package/build/_modules/ai/_modules/ai-mock/_services/aim-mock.service.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/_services/aim-mock.service.js +121 -0
- package/build/_modules/ai/_modules/ai-mock/_services/aim-mock.service.js.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/index.d.ts +14 -0
- package/build/_modules/ai/_modules/ai-mock/index.d.ts.map +1 -0
- package/build/_modules/ai/_modules/ai-mock/index.js +41 -0
- package/build/_modules/ai/_modules/ai-mock/index.js.map +1 -0
- package/build/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.d.ts +31 -3
- package/build/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.d.ts.map +1 -1
- package/build/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.js +71 -33
- package/build/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.js.map +1 -1
- package/build/_modules/ai/_services/ai-embedding-provider.registry.d.ts +1 -1
- package/build/_modules/ai/_services/ai-embedding-provider.registry.d.ts.map +1 -1
- package/build/_modules/ai/_services/ai-embedding-provider.registry.js +1 -1
- package/build/_modules/ai/_services/ai-embedding-provider.registry.js.map +1 -1
- package/build/_modules/ai/_services/ai-llm.service-base.d.ts +16 -0
- package/build/_modules/ai/_services/ai-llm.service-base.d.ts.map +1 -1
- package/build/_modules/ai/_services/ai-llm.service-base.js +65 -0
- package/build/_modules/ai/_services/ai-llm.service-base.js.map +1 -1
- package/build/_modules/ai/index.d.ts +4 -1
- package/build/_modules/ai/index.d.ts.map +1 -1
- package/build/_modules/ai/index.js +9 -1
- package/build/_modules/ai/index.js.map +1 -1
- package/build/_modules/server/server-status/server-status.control-service.d.ts +10 -0
- package/build/_modules/server/server-status/server-status.control-service.d.ts.map +1 -1
- package/build/_modules/server/server-status/server-status.control-service.js +21 -0
- package/build/_modules/server/server-status/server-status.control-service.js.map +1 -1
- package/package.json +11 -2
- package/src/_modules/ai/_collections/ai-model-config-validator.util.spec.ts +150 -0
- package/src/_modules/ai/_collections/ai-model-config-validator.util.ts +131 -0
- package/src/_modules/ai/_collections/ai-provider-error.util.ts +144 -0
- package/src/_modules/ai/_models/interfaces/dynts-ai-provider-error.interface.ts +16 -0
- package/src/_modules/ai/_modules/ai-mock/_collections/aim-default-collection.util.spec.ts +72 -0
- package/src/_modules/ai/_modules/ai-mock/_collections/aim-default-collection.util.ts +71 -0
- package/src/_modules/ai/_modules/ai-mock/_collections/aim-fixture-match.util.spec.ts +57 -0
- package/src/_modules/ai/_modules/ai-mock/_collections/aim-fixture-match.util.ts +60 -0
- package/src/_modules/ai/_modules/ai-mock/_collections/aim-fixture-serializer.util.spec.ts +56 -0
- package/src/_modules/ai/_modules/ai-mock/_collections/aim-fixture-serializer.util.ts +107 -0
- package/src/_modules/ai/_modules/ai-mock/_enums/aim-modality.enum.ts +15 -0
- package/src/_modules/ai/_modules/ai-mock/_models/interfaces/aim-call-input.interface.ts +14 -0
- package/src/_modules/ai/_modules/ai-mock/_models/interfaces/aim-error.interface.ts +11 -0
- package/src/_modules/ai/_modules/ai-mock/_models/interfaces/aim-fixture.interface.ts +30 -0
- package/src/_modules/ai/_modules/ai-mock/_models/interfaces/aim-request.interface.ts +19 -0
- package/src/_modules/ai/_modules/ai-mock/_models/interfaces/aim-service-collection.interface.ts +20 -0
- package/src/_modules/ai/{_services → _modules/ai-mock/_services}/ai-embedding-mock.service.spec.ts +1 -1
- package/src/_modules/ai/{_services → _modules/ai-mock/_services}/ai-embedding-mock.service.ts +2 -2
- package/src/_modules/ai/_modules/ai-mock/_services/aim-fixture-harness.service.spec.ts +134 -0
- package/src/_modules/ai/_modules/ai-mock/_services/aim-fixture-harness.service.ts +127 -0
- package/src/_modules/ai/_modules/ai-mock/_services/aim-fixture-pipeline.spec.ts +105 -0
- package/src/_modules/ai/_modules/ai-mock/_services/aim-fixture-store.service.spec.ts +50 -0
- package/src/_modules/ai/_modules/ai-mock/_services/aim-fixture-store.service.ts +65 -0
- package/src/_modules/ai/_modules/ai-mock/_services/aim-mock.service.spec.ts +112 -0
- package/src/_modules/ai/_modules/ai-mock/_services/aim-mock.service.ts +142 -0
- package/src/_modules/ai/_modules/ai-mock/index.ts +44 -0
- package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.ts +90 -38
- package/src/_modules/ai/_services/ai-embedding-provider.registry.spec.ts +1 -1
- package/src/_modules/ai/_services/ai-embedding-provider.registry.ts +1 -1
- package/src/_modules/ai/_services/ai-llm.service-base.reconciler.spec.ts +133 -0
- package/src/_modules/ai/_services/ai-llm.service-base.ts +80 -0
- package/src/_modules/ai/index.ts +11 -1
- package/src/_modules/server/server-status/server-status.control-service.spec.ts +40 -0
- package/src/_modules/server/server-status/server-status.control-service.ts +40 -0
- package/build/_modules/ai/_services/ai-embedding-mock.service.d.ts.map +0 -1
- package/build/_modules/ai/_services/ai-embedding-mock.service.js.map +0 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
|
|
3
|
+
import { DyNTS_AIMock_Request } from '../_models/interfaces/aim-request.interface';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A fixture-match kulcs DETERMINISZTIKUS képzése. Cél: azonos „jelentésű" request → azonos kulcs,
|
|
7
|
+
* függetlenül a config-kulcsok sorrendjétől és az input whitespace-variánsaitól (különben false
|
|
8
|
+
* reject-on-unknown, l. HYPERPLAN D3 + MP-3 SP-3.1). NEM végez generálást — tiszta, hálózat-mentes.
|
|
9
|
+
*/
|
|
10
|
+
export class DyNTS_AIMock_FixtureMatch_Util {
|
|
11
|
+
/**
|
|
12
|
+
* A teljes match-kulcs: `provider|model|modality|<config-hash>|<input-hash>`.
|
|
13
|
+
* @param request - a mock-hívás bemenete
|
|
14
|
+
*/
|
|
15
|
+
static buildMatchKey(request: DyNTS_AIMock_Request): string {
|
|
16
|
+
const configHash: string = this.hash(this.canonicalizeConfig(request.config ?? {}));
|
|
17
|
+
const inputHash: string = this.buildInputHash(request.input);
|
|
18
|
+
|
|
19
|
+
return `${request.provider}|${request.model}|${request.modality}|${configHash}|${inputHash}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Csak az input-hash (a `DyNTS_AIMock_Fixture.inputHash`-hez): a kanonizált input SHA-256-ja.
|
|
24
|
+
* @param input - a modalitás-függő bemenet
|
|
25
|
+
*/
|
|
26
|
+
static buildInputHash(input: unknown): string {
|
|
27
|
+
return this.hash(this.canonicalizeInput(input));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Kulcs-rendezett, stabil szöveg egy config-objektumból (a config-kulcsok sorrend-függetlenségéhez).
|
|
32
|
+
* Megjegyzés: a nested-objektum mély-kanonizációja jövőbeli finomítás (a shallow key-sort a config-flat
|
|
33
|
+
* paramétereket — temperature/dimensions/size — fedi).
|
|
34
|
+
* @param config - a kanonizálandó config
|
|
35
|
+
*/
|
|
36
|
+
private static canonicalizeConfig(config: Record<string, unknown>): string {
|
|
37
|
+
const sortedKeys: string[] = Object.keys(config).sort();
|
|
38
|
+
const parts: string[] = sortedKeys.map((key: string) => `${key}:${JSON.stringify(config[key])}`);
|
|
39
|
+
|
|
40
|
+
return parts.join('|');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Input-kanonizáció: string → trim + belső whitespace-összevonás; egyéb (pl. embedding-szöveg-tömb) →
|
|
45
|
+
* stabil JSON (a sorrend jelentős, pl. batch-embedding).
|
|
46
|
+
* @param input - a bemenet
|
|
47
|
+
*/
|
|
48
|
+
private static canonicalizeInput(input: unknown): string {
|
|
49
|
+
if (typeof input === 'string') {
|
|
50
|
+
return input.trim().replace(/\s+/g, ' ');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return JSON.stringify(input);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** SHA-256 hex az adott szövegből. */
|
|
57
|
+
private static hash(value: string): string {
|
|
58
|
+
return crypto.createHash('sha256').update(value).digest('hex');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { DyNTS_AIMock_Modality } from '../_enums/aim-modality.enum';
|
|
2
|
+
import { DyNTS_AIMock_Fixture } from '../_models/interfaces/aim-fixture.interface';
|
|
3
|
+
import { DyNTS_AIMock_FixtureSerializer_Util } from './aim-fixture-serializer.util';
|
|
4
|
+
|
|
5
|
+
describe('| DyNTS_AIMock_FixtureSerializer_Util', () => {
|
|
6
|
+
|
|
7
|
+
function fixture(model: string, inputHash: string): DyNTS_AIMock_Fixture {
|
|
8
|
+
return {
|
|
9
|
+
provider: 'open-ai',
|
|
10
|
+
model: model,
|
|
11
|
+
modality: DyNTS_AIMock_Modality.text,
|
|
12
|
+
input: 'ping',
|
|
13
|
+
inputHash: inputHash,
|
|
14
|
+
output: 'pong',
|
|
15
|
+
capturedAt: '2026-07-21T00:00:00.000Z',
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
it('| determinisztikus: a fixture-ok SORRENDJETOL fuggetlenul azonos kimenet', () => {
|
|
20
|
+
const a: DyNTS_AIMock_Fixture[] = [ fixture('m-b', 'h2'), fixture('m-a', 'h1') ];
|
|
21
|
+
const b: DyNTS_AIMock_Fixture[] = [ fixture('m-a', 'h1'), fixture('m-b', 'h2') ];
|
|
22
|
+
|
|
23
|
+
const first: string = DyNTS_AIMock_FixtureSerializer_Util.toConstantFile({ constName: 'X', fixtures: a });
|
|
24
|
+
const second: string = DyNTS_AIMock_FixtureSerializer_Util.toConstantFile({ constName: 'X', fixtures: b });
|
|
25
|
+
|
|
26
|
+
expect(first).toBe(second);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('| az objektum-kulcsok abc-sorrendben jonnek ki (stabil diff)', () => {
|
|
30
|
+
const output: string = DyNTS_AIMock_FixtureSerializer_Util.toConstantFile({
|
|
31
|
+
constName: 'X',
|
|
32
|
+
fixtures: [ fixture('m-a', 'h1') ],
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(output.indexOf('"capturedAt"')).toBeLessThan(output.indexOf('"input"'));
|
|
36
|
+
expect(output.indexOf('"input"')).toBeLessThan(output.indexOf('"model"'));
|
|
37
|
+
expect(output.indexOf('"model"')).toBeLessThan(output.indexOf('"provider"'));
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('| valid TS-konstans vazat ad (import + export const + tipus)', () => {
|
|
41
|
+
const output: string = DyNTS_AIMock_FixtureSerializer_Util.toConstantFile({
|
|
42
|
+
constName: 'DyNTS_AIMock_TestFixtures',
|
|
43
|
+
fixtures: [ fixture('m-a', 'h1') ],
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(output).toContain(`from '@futdevpro/nts-dynamo/ai/ai-mock'`);
|
|
47
|
+
expect(output).toContain('export const DyNTS_AIMock_TestFixtures: DyNTS_AIMock_Fixture[] = [');
|
|
48
|
+
expect(output.trim().endsWith('];')).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('| ures keszlet → ures tomb-literal', () => {
|
|
52
|
+
const output: string = DyNTS_AIMock_FixtureSerializer_Util.toConstantFile({ constName: 'X', fixtures: [] });
|
|
53
|
+
|
|
54
|
+
expect(output).toContain('export const X: DyNTS_AIMock_Fixture[] = [');
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { DyNTS_AIMock_Fixture } from '../_models/interfaces/aim-fixture.interface';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Beegetett fixture-ok **determinisztikus** TS-konstans fajlla alakitasa (D6: git-tracked, NEM env-fuggo).
|
|
5
|
+
*
|
|
6
|
+
* A determinizmus itt nem kozmetika: a fixture-fajlok verziokezeltek, ezert ugyanaz a fixture-keszlet
|
|
7
|
+
* MINDIG ugyanazt a fajl-tartalmat kell adja — kulonben minden ujra-generalas zajos diff-et okoz, es a
|
|
8
|
+
* code-review hasznalhatatlanna valik. Ezert: a fixture-ok **match-kulcs szerint rendezve**, az objektum-
|
|
9
|
+
* kulcsok pedig **rekurzivan abc-sorrendben** kerulnek ki.
|
|
10
|
+
*/
|
|
11
|
+
export class DyNTS_AIMock_FixtureSerializer_Util {
|
|
12
|
+
|
|
13
|
+
/** Behuzas egy szintje. */
|
|
14
|
+
private static readonly indentUnit: string = ' ';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Fixture-tomb → teljes TS-konstans fajl tartalma (import + export const + JSDoc-fejlec).
|
|
18
|
+
* @param set - a konstans neve + a fixture-ok + opcionalis fejlec-megjegyzes
|
|
19
|
+
*/
|
|
20
|
+
static toConstantFile(
|
|
21
|
+
set: {
|
|
22
|
+
constName: string,
|
|
23
|
+
fixtures: DyNTS_AIMock_Fixture[],
|
|
24
|
+
headerNote?: string,
|
|
25
|
+
}
|
|
26
|
+
): string {
|
|
27
|
+
const sorted: DyNTS_AIMock_Fixture[] = this.sortFixtures(set.fixtures);
|
|
28
|
+
const body: string = sorted
|
|
29
|
+
.map((fixture: DyNTS_AIMock_Fixture) => this.stringify(fixture, this.indentUnit))
|
|
30
|
+
.join(',\n');
|
|
31
|
+
|
|
32
|
+
const note: string = set.headerNote ?? 'Auto-generalt fixture-keszlet — kezzel NE szerkeszd.';
|
|
33
|
+
|
|
34
|
+
return [
|
|
35
|
+
`import { DyNTS_AIMock_Fixture } from '@futdevpro/nts-dynamo/ai/ai-mock';`,
|
|
36
|
+
'',
|
|
37
|
+
'/**',
|
|
38
|
+
` * ${note}`,
|
|
39
|
+
' *',
|
|
40
|
+
' * Determinisztikus sorrend (match-kulcs szerint) + rendezett objektum-kulcsok, hogy az ujra-generalas',
|
|
41
|
+
' * ne okozzon zajos diff-et. Forras: `DyNTS_AIMock_FixtureSerializer_Util`.',
|
|
42
|
+
' */',
|
|
43
|
+
`export const ${set.constName}: DyNTS_AIMock_Fixture[] = [`,
|
|
44
|
+
body,
|
|
45
|
+
'];',
|
|
46
|
+
'',
|
|
47
|
+
].join('\n');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Fixture-ok stabil rendezese (provider|model|modality|inputHash). */
|
|
51
|
+
private static sortFixtures(fixtures: DyNTS_AIMock_Fixture[]): DyNTS_AIMock_Fixture[] {
|
|
52
|
+
return [ ...fixtures ].sort((a: DyNTS_AIMock_Fixture, b: DyNTS_AIMock_Fixture) => {
|
|
53
|
+
return this.sortKeyOf(a).localeCompare(this.sortKeyOf(b));
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** A rendezesi kulcs egy fixture-hoz. */
|
|
58
|
+
private static sortKeyOf(fixture: DyNTS_AIMock_Fixture): string {
|
|
59
|
+
return `${fixture.provider}|${fixture.model}|${fixture.modality}|${fixture.inputHash}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Rekurziv, kulcs-rendezett stringify (a `JSON.stringify` a beszurasi sorrendet tartana meg — az nem stabil).
|
|
64
|
+
* @param value - a szerializalando ertek
|
|
65
|
+
* @param indent - az aktualis behuzas
|
|
66
|
+
*/
|
|
67
|
+
private static stringify(value: unknown, indent: string): string {
|
|
68
|
+
if (value === null || value === undefined) {
|
|
69
|
+
return `${indent}null`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (Array.isArray(value)) {
|
|
73
|
+
if (!value.length) {
|
|
74
|
+
return `${indent}[]`;
|
|
75
|
+
}
|
|
76
|
+
const items: string = value
|
|
77
|
+
.map((item: unknown) => this.stringify(item, indent + this.indentUnit))
|
|
78
|
+
.join(',\n');
|
|
79
|
+
|
|
80
|
+
return `${indent}[\n${items}\n${indent}]`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (typeof value === 'object') {
|
|
84
|
+
// `Object.entries` a generikus `object`-en is mukodik → nincs szukseg `as`-castra.
|
|
85
|
+
const source: Map<string, unknown> = new Map(Object.entries(value));
|
|
86
|
+
const keys: string[] = [ ...source.keys() ]
|
|
87
|
+
.filter((key: string) => source.get(key) !== undefined)
|
|
88
|
+
.sort();
|
|
89
|
+
|
|
90
|
+
if (!keys.length) {
|
|
91
|
+
return `${indent}{}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const entries: string = keys
|
|
95
|
+
.map((key: string) => {
|
|
96
|
+
const rendered: string = this.stringify(source.get(key), indent + this.indentUnit).trimStart();
|
|
97
|
+
|
|
98
|
+
return `${indent}${this.indentUnit}${JSON.stringify(key)}: ${rendered}`;
|
|
99
|
+
})
|
|
100
|
+
.join(',\n');
|
|
101
|
+
|
|
102
|
+
return `${indent}{\n${entries}\n${indent}}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return `${indent}${JSON.stringify(value)}`;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Az AI-mock által kezelt modalitások. A mock MINDEN modalitásra ugyanazt a replay / reject-on-unknown
|
|
3
|
+
* viselkedést adja (l. `documentations/plans/ai-mocking-system/HYPERPLAN.md` D3). A `value` a beégetett
|
|
4
|
+
* fixture-ök + a match-kulcs modality-szegmense.
|
|
5
|
+
*/
|
|
6
|
+
export enum DyNTS_AIMock_Modality {
|
|
7
|
+
/** Szöveg-completion (chat / LLM). */
|
|
8
|
+
text = 'text',
|
|
9
|
+
/** Embedding-vektor kimenet. */
|
|
10
|
+
embedding = 'embedding',
|
|
11
|
+
/** Kép-generálás (base64 kimenet). */
|
|
12
|
+
image = 'image',
|
|
13
|
+
/** Hang: transcription (audio→szöveg) vagy TTS (szöveg→base64-audio). */
|
|
14
|
+
audio = 'audio',
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Egy mock modalitás-hívás bemenete — a modalitást a hívott metódus adja (`chatCompletion` → text stb.),
|
|
3
|
+
* ezért itt NINCS `modality` mező (v.ö. `DyNTS_AIMock_Request`, ami a teljes match-kulcs bemenete).
|
|
4
|
+
*/
|
|
5
|
+
export interface DyNTS_AIMock_CallInput {
|
|
6
|
+
/** Provider-azonosító (pl. 'open-ai'). */
|
|
7
|
+
provider: string;
|
|
8
|
+
/** Pontos API modell-azonosító (pl. 'gpt-4o-mini'). */
|
|
9
|
+
model: string;
|
|
10
|
+
/** Provider-specifikus config-paraméterek. */
|
|
11
|
+
config?: Record<string, unknown>;
|
|
12
|
+
/** Modalitás-függő bemenet. */
|
|
13
|
+
input: unknown;
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { DyNTS_AI_ProviderError } from '../../../../_models/interfaces/dynts-ai-provider-error.interface';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Provider-faithful hiba egy fixture-höz — a mock UGYANEZT reprodukálja, mintha a valódi provider dobná
|
|
5
|
+
* (pl. reasoning-modell + `temperature` → OpenAI HTTP 400 `unsupported_parameter`).
|
|
6
|
+
*
|
|
7
|
+
* **Alias** a modul-szintű `DyNTS_AI_ProviderError`-ra (SSoT): a Layer A validátor
|
|
8
|
+
* (`DyNTS_AI_ModelConfig_Validator`) és a Layer B fixture-ök UGYANAZT a hiba-alakot használják — nincs
|
|
9
|
+
* párhuzamos shape. A név megmarad, hogy az ai-mock oldali olvashatóság ne romoljon.
|
|
10
|
+
*/
|
|
11
|
+
export type DyNTS_AIMock_Error = DyNTS_AI_ProviderError;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { DyNTS_AIMock_Modality } from '../../_enums/aim-modality.enum';
|
|
2
|
+
import { DyNTS_AIMock_Error } from './aim-error.interface';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Egy beégetett fixture-rekord (git-tracked konstans, D6). Az `output` XOR `error` közül PONTOSAN egy van:
|
|
6
|
+
* sikeres válasz VAGY provider-faithful hiba. Az `inputHash` a normalizált match-kulcs input-szegmense
|
|
7
|
+
* (l. `DyNTS_AIMock_FixtureMatch_Util.buildInputHash`). A `capturedAt` / `sourceModel` audit-nyomvonal (D4).
|
|
8
|
+
*/
|
|
9
|
+
export interface DyNTS_AIMock_Fixture {
|
|
10
|
+
/** Provider-azonosító. */
|
|
11
|
+
provider: string;
|
|
12
|
+
/** Pontos API modell-azonosító. */
|
|
13
|
+
model: string;
|
|
14
|
+
/** A fixture modalitása. */
|
|
15
|
+
modality: DyNTS_AIMock_Modality;
|
|
16
|
+
/** A rögzített config-paraméterek (a match-kulcs része). */
|
|
17
|
+
config?: Record<string, unknown>;
|
|
18
|
+
/** A normalizált input-hash (deterministic input→fixture feloldáshoz). */
|
|
19
|
+
inputHash: string;
|
|
20
|
+
/** A nyers bemenet (ember-olvasható referencia; a match az `inputHash`-en megy). */
|
|
21
|
+
input: unknown;
|
|
22
|
+
/** Sikeres kimenet (modalitás-függő). Az `error`-ral kölcsönösen kizáró. */
|
|
23
|
+
output?: unknown;
|
|
24
|
+
/** Provider-faithful hiba. Az `output`-tal kölcsönösen kizáró. */
|
|
25
|
+
error?: DyNTS_AIMock_Error;
|
|
26
|
+
/** A beégetés időbélyege (ISO). */
|
|
27
|
+
capturedAt: string;
|
|
28
|
+
/** A tényleges generáló modell (ha eltér a kért aliastól). */
|
|
29
|
+
sourceModel?: string;
|
|
30
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DyNTS_AIMock_Modality } from '../../_enums/aim-modality.enum';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Egy mock-hívás bemenete — EBBŐL képződik a fixture-match kulcs (`DyNTS_AIMock_FixtureMatch_Util`).
|
|
5
|
+
* A `config` a provider-specifikus hívás-paraméterek (a FR-048 `settingsSchema` ellen validálandó), az
|
|
6
|
+
* `input` modalitás-függő (prompt-szöveg / embedding-szöveg(ek) / kép-prompt / audio-referencia).
|
|
7
|
+
*/
|
|
8
|
+
export interface DyNTS_AIMock_Request {
|
|
9
|
+
/** Provider-azonosító (pl. 'open-ai', 'anthropic', 'fdp-ai'). */
|
|
10
|
+
provider: string;
|
|
11
|
+
/** Pontos API modell-azonosító (pl. 'gpt-4o-mini'). */
|
|
12
|
+
model: string;
|
|
13
|
+
/** A hívás modalitása. */
|
|
14
|
+
modality: DyNTS_AIMock_Modality;
|
|
15
|
+
/** Provider-specifikus config-paraméterek (temperature / dimensions / size / …). */
|
|
16
|
+
config?: Record<string, unknown>;
|
|
17
|
+
/** Modalitás-függő bemenet (prompt / szövegek / kép-prompt / audio-ref). */
|
|
18
|
+
input: unknown;
|
|
19
|
+
}
|
package/src/_modules/ai/_modules/ai-mock/_models/interfaces/aim-service-collection.interface.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { DyFM_AI_ModelInfo } from '@futdevpro/fsm-dynamo/ai';
|
|
2
|
+
|
|
3
|
+
import { DyNTS_AIMock_FixtureStore_Service } from '../../_services/aim-fixture-store.service';
|
|
4
|
+
import { DyNTS_AIMock_Service } from '../../_services/aim-mock.service';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Egy kesz, osszedrotozott AI-mock keszlet (`getDefaultAiMockServiceCollection` kimenete): a fixture-tar,
|
|
8
|
+
* a ra epulo mock-provider, es a config-validaciohoz bekototott modell-registry.
|
|
9
|
+
*
|
|
10
|
+
* A `store` azert is publikus, hogy a teszt futas kozben tovabbi fixture-oket regisztralhasson
|
|
11
|
+
* (`store.register([...])`) anelkul, hogy ujra kellene epiteni a kollekciot.
|
|
12
|
+
*/
|
|
13
|
+
export interface DyNTS_AIMock_ServiceCollection {
|
|
14
|
+
/** A beegetett fixture-ok tara (futas kozben bovitheto). */
|
|
15
|
+
store: DyNTS_AIMock_FixtureStore_Service;
|
|
16
|
+
/** A mock provider-surface (chat / embedding / image / audio). */
|
|
17
|
+
service: DyNTS_AIMock_Service;
|
|
18
|
+
/** A bekototott modell-registry — ebbol jon a FR-048 `settingsSchema`-alapu config-validacio. */
|
|
19
|
+
registry: DyFM_AI_ModelInfo[];
|
|
20
|
+
}
|
package/src/_modules/ai/{_services → _modules/ai-mock/_services}/ai-embedding-mock.service.spec.ts
RENAMED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
DyNTS_AI_Embedding_MockService,
|
|
6
6
|
DyNTS_AI_MOCK_EMBEDDING_DIMS,
|
|
7
7
|
} from './ai-embedding-mock.service';
|
|
8
|
-
import { DyNTS_AI_CostEvent } from '
|
|
8
|
+
import { DyNTS_AI_CostEvent } from '../../../_models/interfaces/dynts-ai-cost-event.interface';
|
|
9
9
|
|
|
10
10
|
describe('| DyNTS_AI_Embedding_MockService', () => {
|
|
11
11
|
|
package/src/_modules/ai/{_services → _modules/ai-mock/_services}/ai-embedding-mock.service.ts
RENAMED
|
@@ -4,8 +4,8 @@ import { CreateEmbeddingResponse } from 'openai/resources';
|
|
|
4
4
|
import { DyFM_AI_Provider, DyFM_AI_ProviderCapabilities, DyFM_AI_Config } from '@futdevpro/fsm-dynamo/ai';
|
|
5
5
|
import { DyFM_DAI_EmbeddingInfo } from '@futdevpro/fsm-dynamo/ai/document-ai';
|
|
6
6
|
|
|
7
|
-
import { DyNTS_AI_CostEventCallback } from '
|
|
8
|
-
import { DyNTS_AI_Embedding_ServiceBase } from '
|
|
7
|
+
import { DyNTS_AI_CostEventCallback } from '../../../_models/interfaces/dynts-ai-cost-event-callback.interface';
|
|
8
|
+
import { DyNTS_AI_Embedding_ServiceBase } from '../../../_services/ai-embedding.service-base';
|
|
9
9
|
|
|
10
10
|
/** A mock-embedding default dimenziója (kicsi, hogy a teszt-pool gyors legyen, de elég a koszinuszhoz). */
|
|
11
11
|
export const DyNTS_AI_MOCK_EMBEDDING_DIMS: number = 64;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { DyFM_Error } from '@futdevpro/fsm-dynamo';
|
|
2
|
+
import { DyFM_AI_ModelInfo, DyFM_AI_ModelType, DyFM_AI_Provider } from '@futdevpro/fsm-dynamo/ai';
|
|
3
|
+
|
|
4
|
+
import { DyNTS_AIMock_Modality } from '../_enums/aim-modality.enum';
|
|
5
|
+
import { DyNTS_AIMock_Request } from '../_models/interfaces/aim-request.interface';
|
|
6
|
+
import { DyNTS_AIMock_Fixture } from '../_models/interfaces/aim-fixture.interface';
|
|
7
|
+
import { DyNTS_AIMock_FixtureHarness_Service } from './aim-fixture-harness.service';
|
|
8
|
+
|
|
9
|
+
describe('| DyNTS_AIMock_FixtureHarness_Service (MP-3 SP-3.2)', () => {
|
|
10
|
+
|
|
11
|
+
const registry: DyFM_AI_ModelInfo[] = [
|
|
12
|
+
{
|
|
13
|
+
id: 'cheap-mini',
|
|
14
|
+
provider: DyFM_AI_Provider.OpenAI,
|
|
15
|
+
modelType: DyFM_AI_ModelType.Chat,
|
|
16
|
+
tier: 'mini',
|
|
17
|
+
capabilities: {} as any,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'expensive-pro',
|
|
21
|
+
provider: DyFM_AI_Provider.OpenAI,
|
|
22
|
+
modelType: DyFM_AI_ModelType.Chat,
|
|
23
|
+
tier: 'pro',
|
|
24
|
+
capabilities: {} as any,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 'expensive-opus',
|
|
28
|
+
provider: DyFM_AI_Provider.Anthropic,
|
|
29
|
+
modelType: DyFM_AI_ModelType.Chat,
|
|
30
|
+
tier: 'Opus',
|
|
31
|
+
capabilities: {} as any,
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/** Stub-generator, ami JELZI, ha meghivtak — igy bizonyithato, hogy a zarak MEG a hivas elott fognak. */
|
|
36
|
+
function makeStub(): { calls: number, generate: (request: DyNTS_AIMock_Request) => Promise<unknown> } {
|
|
37
|
+
const state: { calls: number, generate: (request: DyNTS_AIMock_Request) => Promise<unknown> } = {
|
|
38
|
+
calls: 0,
|
|
39
|
+
generate: async (): Promise<unknown> => {
|
|
40
|
+
state.calls++;
|
|
41
|
+
|
|
42
|
+
return 'generated-output';
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return state;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function request(model: string): DyNTS_AIMock_Request {
|
|
50
|
+
return {
|
|
51
|
+
provider: 'open-ai',
|
|
52
|
+
model: model,
|
|
53
|
+
modality: DyNTS_AIMock_Modality.text,
|
|
54
|
+
config: { maxTokens: 10 },
|
|
55
|
+
input: 'ping',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
it('| spend-guard ALAPBOL kikapcsolt → nem general, ES a generatort MEG CSAK NEM IS hivja', async () => {
|
|
60
|
+
const stub = makeStub();
|
|
61
|
+
const harness: DyNTS_AIMock_FixtureHarness_Service = new DyNTS_AIMock_FixtureHarness_Service({
|
|
62
|
+
generator: stub.generate,
|
|
63
|
+
registry: registry,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
await harness.capture(request('cheap-mini'));
|
|
68
|
+
fail('spend-guard hibat vartunk');
|
|
69
|
+
} catch (error) {
|
|
70
|
+
expect(error instanceof DyFM_Error).toBe(true);
|
|
71
|
+
if (error instanceof DyFM_Error) {
|
|
72
|
+
expect(error.message).toContain('spend-guard');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
expect(stub.calls).toBe(0);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('| D2 tier-cap: `pro` tier elutasitva, a generator nem hivodik', async () => {
|
|
80
|
+
const stub = makeStub();
|
|
81
|
+
const harness: DyNTS_AIMock_FixtureHarness_Service = new DyNTS_AIMock_FixtureHarness_Service({
|
|
82
|
+
generator: stub.generate,
|
|
83
|
+
registry: registry,
|
|
84
|
+
allowRealSpend: true,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await expectAsync(harness.capture(request('expensive-pro'))).toBeRejected();
|
|
88
|
+
expect(stub.calls).toBe(0);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('| D2 tier-cap: `Opus` tier elutasitva (kis/nagybetu-fuggetlen)', async () => {
|
|
92
|
+
const stub = makeStub();
|
|
93
|
+
const harness: DyNTS_AIMock_FixtureHarness_Service = new DyNTS_AIMock_FixtureHarness_Service({
|
|
94
|
+
generator: stub.generate,
|
|
95
|
+
registry: registry,
|
|
96
|
+
allowRealSpend: true,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
await expectAsync(harness.capture(request('expensive-opus'))).toBeRejected();
|
|
100
|
+
expect(stub.calls).toBe(0);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('| registry-ben nem szereplo modell → elutasitva (a tier nem igazolhato)', async () => {
|
|
104
|
+
const stub = makeStub();
|
|
105
|
+
const harness: DyNTS_AIMock_FixtureHarness_Service = new DyNTS_AIMock_FixtureHarness_Service({
|
|
106
|
+
generator: stub.generate,
|
|
107
|
+
registry: registry,
|
|
108
|
+
allowRealSpend: true,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await expectAsync(harness.capture(request('nincs-ilyen'))).toBeRejected();
|
|
112
|
+
expect(stub.calls).toBe(0);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('| engedelyezett spend + olcso tier → fixture keszul, helyes mezokkel', async () => {
|
|
116
|
+
const stub = makeStub();
|
|
117
|
+
const harness: DyNTS_AIMock_FixtureHarness_Service = new DyNTS_AIMock_FixtureHarness_Service({
|
|
118
|
+
generator: stub.generate,
|
|
119
|
+
registry: registry,
|
|
120
|
+
allowRealSpend: true,
|
|
121
|
+
nowIso: (): string => '2026-07-21T00:00:00.000Z',
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const fixture: DyNTS_AIMock_Fixture = await harness.capture(request('cheap-mini'));
|
|
125
|
+
|
|
126
|
+
expect(stub.calls).toBe(1);
|
|
127
|
+
expect(fixture.output).toBe('generated-output');
|
|
128
|
+
expect(fixture.model).toBe('cheap-mini');
|
|
129
|
+
expect(fixture.modality).toBe(DyNTS_AIMock_Modality.text);
|
|
130
|
+
expect(fixture.capturedAt).toBe('2026-07-21T00:00:00.000Z');
|
|
131
|
+
expect(fixture.sourceModel).toBe('cheap-mini');
|
|
132
|
+
expect(fixture.inputHash.length).toBeGreaterThan(0);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { DyFM_Error } from '@futdevpro/fsm-dynamo';
|
|
2
|
+
import { DyFM_AI_ModelInfo, DyFM_AI_ModelRegistry_Util } from '@futdevpro/fsm-dynamo/ai';
|
|
3
|
+
|
|
4
|
+
import { DyNTS_AIMock_Request } from '../_models/interfaces/aim-request.interface';
|
|
5
|
+
import { DyNTS_AIMock_Fixture } from '../_models/interfaces/aim-fixture.interface';
|
|
6
|
+
import { DyNTS_AIMock_FixtureMatch_Util } from '../_collections/aim-fixture-match.util';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A D2 kolseg-cap altal TILTOTT modell-tier-ek (kisbetusen hasonlitva).
|
|
10
|
+
*
|
|
11
|
+
* Forras — a FR (`USER-INPUT.md`, 2026-06-22): „nem megyunk el a legmagasabb szintu modellekig, illetve nem
|
|
12
|
+
* megyunk el peldaul OpenAI Pro modellekig, de az alap, default es mini mikro modelleket szeretnem".
|
|
13
|
+
* Ezert tiltott: `pro` (gpt-5.5-pro / o3-pro / gemini-pro), `opus` (claude-opus), `flagship` (a legmagasabb
|
|
14
|
+
* szintu alap-modell). Engedett: mini / nano / legacy / flash / flash-lite / sonnet / haiku.
|
|
15
|
+
*/
|
|
16
|
+
const D2_BLOCKED_TIERS: string[] = [ 'pro', 'opus', 'flagship' ];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* **Fixture-generalo harness** (MP-3 SP-3.2) — VALODI provider-valaszbol keszit beegetheto fixture-t.
|
|
20
|
+
*
|
|
21
|
+
* **Ket egymastol fuggetlen biztonsagi zar, mert ez az egyetlen pont, ahol valodi PENZ folyhat el:**
|
|
22
|
+
* 1. **Spend-guard** — az `allowRealSpend` alapertelmezese `false`. Amig nincs EXPLICIT bekapcsolva, a harness
|
|
23
|
+
* **el sem hivja** az injektalt generatort. Igy meg egy veletlenul bekototott valodi generator sem tud
|
|
24
|
+
* hivast inditani. (G1 kapu: a bekapcsolas user-jovahagyashoz kotott.)
|
|
25
|
+
* 2. **D2 tier-cap** — a Pro / Opus / flagship tier-u modelleket elutasitja MEG a generator hivasa elott.
|
|
26
|
+
*
|
|
27
|
+
* A generator INJEKTALT (`(request) => Promise<unknown>`), ezert a harness maga soha nem fuggo halozattol —
|
|
28
|
+
* a tesztek stub-generatorral futnak, valodi hivas nelkul.
|
|
29
|
+
*/
|
|
30
|
+
export class DyNTS_AIMock_FixtureHarness_Service {
|
|
31
|
+
|
|
32
|
+
/** A valodi (vagy stub) generalo fuggveny — a harness NEM tud halozatot hivni sajat maga. */
|
|
33
|
+
private readonly generator: (request: DyNTS_AIMock_Request) => Promise<unknown>;
|
|
34
|
+
|
|
35
|
+
/** Modell-registry a tier-cap ellenorzeshez. */
|
|
36
|
+
private readonly registry: DyFM_AI_ModelInfo[];
|
|
37
|
+
|
|
38
|
+
/** Spend-guard: amig `false`, a generator NEM hivodik meg. Default: `false`. */
|
|
39
|
+
private readonly allowRealSpend: boolean;
|
|
40
|
+
|
|
41
|
+
/** Tiltott tier-ek (default: a D2 keszlet). */
|
|
42
|
+
private readonly blockedTiers: string[];
|
|
43
|
+
|
|
44
|
+
/** ISO-timestamp forras (injektalhato a determinisztikus tesztekhez). */
|
|
45
|
+
private readonly nowIso: () => string;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @param set - generator + registry + a ket biztonsagi zar beallitasa.
|
|
49
|
+
*/
|
|
50
|
+
constructor(
|
|
51
|
+
set: {
|
|
52
|
+
generator: (request: DyNTS_AIMock_Request) => Promise<unknown>,
|
|
53
|
+
registry?: DyFM_AI_ModelInfo[],
|
|
54
|
+
/** CSAK explicit `true` eseten hivodik meg a generator (G1). Default: `false`. */
|
|
55
|
+
allowRealSpend?: boolean,
|
|
56
|
+
blockedTiers?: string[],
|
|
57
|
+
nowIso?: () => string,
|
|
58
|
+
}
|
|
59
|
+
) {
|
|
60
|
+
this.generator = set.generator;
|
|
61
|
+
this.registry = set.registry ?? [];
|
|
62
|
+
this.allowRealSpend = set.allowRealSpend === true;
|
|
63
|
+
this.blockedTiers = set.blockedTiers ?? D2_BLOCKED_TIERS;
|
|
64
|
+
this.nowIso = set.nowIso ?? ((): string => new Date().toISOString());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Egy fixture rogzitese valodi generalasbol. A ket zar sorrendje: spend-guard → tier-cap → generator.
|
|
69
|
+
* @param request - a rogzitendo hivas (provider + model + modality + config + input)
|
|
70
|
+
*/
|
|
71
|
+
async capture(request: DyNTS_AIMock_Request): Promise<DyNTS_AIMock_Fixture> {
|
|
72
|
+
this.assertSpendAllowed(request);
|
|
73
|
+
this.assertTierAllowed(request);
|
|
74
|
+
|
|
75
|
+
const output: unknown = await this.generator(request);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
provider: request.provider,
|
|
79
|
+
model: request.model,
|
|
80
|
+
modality: request.modality,
|
|
81
|
+
config: request.config,
|
|
82
|
+
input: request.input,
|
|
83
|
+
inputHash: DyNTS_AIMock_FixtureMatch_Util.buildInputHash(request.input),
|
|
84
|
+
output: output,
|
|
85
|
+
capturedAt: this.nowIso(),
|
|
86
|
+
sourceModel: request.model,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** 1. zar — a spend-guard. */
|
|
91
|
+
private assertSpendAllowed(request: DyNTS_AIMock_Request): void {
|
|
92
|
+
if (this.allowRealSpend) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
throw new DyFM_Error({
|
|
97
|
+
message: `[ai-mock] real generation is DISABLED (spend-guard). Model '${request.provider}:${request.model}'. ` +
|
|
98
|
+
'Set allowRealSpend=true only with explicit approval (G1 gate).',
|
|
99
|
+
userMessage: 'Real AI generation is disabled.',
|
|
100
|
+
errorCode: 'DyNTS-AIMOCK-SPEND',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** 2. zar — a D2 tier-cap. Ismeretlen modell (nincs registry-ben) → NEM engedjuk (honesty). */
|
|
105
|
+
private assertTierAllowed(request: DyNTS_AIMock_Request): void {
|
|
106
|
+
const modelInfo: DyFM_AI_ModelInfo = DyFM_AI_ModelRegistry_Util.findModelInfo(this.registry, request.model);
|
|
107
|
+
const tier: string = (modelInfo?.tier ?? '').toLowerCase();
|
|
108
|
+
|
|
109
|
+
if (!modelInfo) {
|
|
110
|
+
throw new DyFM_Error({
|
|
111
|
+
message: `[ai-mock] model '${request.model}' is not in the registry — tier cannot be verified, ` +
|
|
112
|
+
'refusing to generate (D2 cost-cap, honesty).',
|
|
113
|
+
userMessage: 'Unknown AI model for fixture generation.',
|
|
114
|
+
errorCode: 'DyNTS-AIMOCK-TIER',
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (this.blockedTiers.includes(tier)) {
|
|
119
|
+
throw new DyFM_Error({
|
|
120
|
+
message: `[ai-mock] model '${request.model}' has blocked tier '${modelInfo.tier}' (D2 cost-cap: ` +
|
|
121
|
+
`${this.blockedTiers.join(', ')}). Use a mini/nano/flash tier model for fixtures.`,
|
|
122
|
+
userMessage: 'This AI model tier is not allowed for fixture generation.',
|
|
123
|
+
errorCode: 'DyNTS-AIMOCK-TIER',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|