Skip to content

Commit d2c06b9

Browse files
committed
refactor(transformations): split into separate files and simplify inheritance
- Split transformation-steps.ts and transformations.ts - Remove TransformationStepBase: V1 defines all fields, V2 extends V1 - Remove TransformationBase: V1 defines container + steps, V2 extends V1 - TransformationV1 is now arktype validator (was plain TS type) - Factory functions return alias types (Transformation, TransformationStep) - Inline migrateStepV1ToV2 into .pipe() callback Schema shapes and migration behavior unchanged.
1 parent 59ccf08 commit d2c06b9

File tree

3 files changed

+161
-147
lines changed

3 files changed

+161
-147
lines changed

apps/whispering/src/lib/services/db/models/index.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,12 @@ export {
2020
TransformationStepRunFailed,
2121
TransformationStepRunRunning,
2222
} from './transformation-runs';
23-
export type {
24-
TransformationStepV1,
25-
TransformationStepV2,
26-
TransformationV1,
27-
TransformationV2,
28-
} from './transformations';
29-
// Transformations
23+
// Transformation Steps
24+
export type { TransformationStepV1, TransformationStepV2 } from './transformation-steps';
3025
export {
31-
generateDefaultTransformation,
3226
generateDefaultTransformationStep,
33-
Transformation,
3427
TransformationStep,
35-
} from './transformations';
28+
} from './transformation-steps';
29+
// Transformations
30+
export type { TransformationV1, TransformationV2 } from './transformations';
31+
export { generateDefaultTransformation, Transformation } from './transformations';
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { type } from 'arktype';
2+
import { nanoid } from 'nanoid/non-secure';
3+
import { TRANSFORMATION_STEP_TYPES } from '$lib/constants/database';
4+
import {
5+
ANTHROPIC_INFERENCE_MODELS,
6+
GOOGLE_INFERENCE_MODELS,
7+
GROQ_INFERENCE_MODELS,
8+
INFERENCE_PROVIDER_IDS,
9+
OPENAI_INFERENCE_MODELS,
10+
} from '$lib/constants/inference';
11+
12+
/**
13+
* The current version of the TransformationStep schema.
14+
* Increment this when adding new fields or making breaking changes.
15+
*/
16+
const CURRENT_TRANSFORMATION_STEP_VERSION = 2 as const;
17+
18+
// ============================================================================
19+
// VERSION 1 (FROZEN)
20+
// ============================================================================
21+
22+
/**
23+
* V1: Original schema without Custom provider fields.
24+
* Old data has no version field, so we default to 1.
25+
*
26+
* FROZEN: Do not modify. This represents the historical V1 schema.
27+
*/
28+
export const TransformationStepV1 = type({
29+
id: 'string',
30+
type: type.enumerated(...TRANSFORMATION_STEP_TYPES),
31+
'prompt_transform.inference.provider': type.enumerated(
32+
...INFERENCE_PROVIDER_IDS,
33+
),
34+
'prompt_transform.inference.provider.OpenAI.model': type.enumerated(
35+
...OPENAI_INFERENCE_MODELS,
36+
),
37+
'prompt_transform.inference.provider.Groq.model': type.enumerated(
38+
...GROQ_INFERENCE_MODELS,
39+
),
40+
'prompt_transform.inference.provider.Anthropic.model': type.enumerated(
41+
...ANTHROPIC_INFERENCE_MODELS,
42+
),
43+
'prompt_transform.inference.provider.Google.model': type.enumerated(
44+
...GOOGLE_INFERENCE_MODELS,
45+
),
46+
// OpenRouter model is a free string (user can enter any model)
47+
'prompt_transform.inference.provider.OpenRouter.model': 'string',
48+
'prompt_transform.systemPromptTemplate': 'string',
49+
'prompt_transform.userPromptTemplate': 'string',
50+
'find_replace.findText': 'string',
51+
'find_replace.replaceText': 'string',
52+
'find_replace.useRegex': 'boolean',
53+
version: '1 = 1',
54+
});
55+
56+
export type TransformationStepV1 = typeof TransformationStepV1.infer;
57+
58+
// ============================================================================
59+
// VERSION 2 (CURRENT)
60+
// ============================================================================
61+
62+
/**
63+
* V2: Added Custom provider fields for local LLM endpoints.
64+
* Extends V1 with:
65+
* - Custom.model: Model name for custom endpoints
66+
* - Custom.baseUrl: Per-step base URL (falls back to global setting)
67+
*
68+
* CURRENT VERSION: This is the latest schema.
69+
*/
70+
export const TransformationStepV2 = TransformationStepV1.merge({
71+
version: '2',
72+
/** Custom provider for local LLM endpoints (Ollama, LM Studio, llama.cpp, etc.) */
73+
'prompt_transform.inference.provider.Custom.model': 'string',
74+
/**
75+
* Per-step base URL for custom endpoints. Allows different steps to use
76+
* different local services (e.g., Ollama on :11434, LM Studio on :1234).
77+
* Falls back to global `completion.Custom.baseUrl` setting if empty.
78+
*/
79+
'prompt_transform.inference.provider.Custom.baseUrl': 'string',
80+
});
81+
82+
export type TransformationStepV2 = typeof TransformationStepV2.infer;
83+
84+
// ============================================================================
85+
// MIGRATING VALIDATOR
86+
// ============================================================================
87+
88+
/**
89+
* TransformationStep validator with automatic migration.
90+
* Accepts V1 or V2 and always outputs V2.
91+
*/
92+
export const TransformationStep = TransformationStepV1.or(
93+
TransformationStepV2,
94+
).pipe((step): TransformationStepV2 => {
95+
if (step.version === 1) {
96+
return {
97+
...step,
98+
version: 2,
99+
'prompt_transform.inference.provider.Custom.model': '',
100+
'prompt_transform.inference.provider.Custom.baseUrl': '',
101+
};
102+
}
103+
return step;
104+
});
105+
106+
export type TransformationStep = TransformationStepV2;
107+
108+
// ============================================================================
109+
// FACTORY FUNCTION
110+
// ============================================================================
111+
112+
export function generateDefaultTransformationStep(): TransformationStep {
113+
return {
114+
version: CURRENT_TRANSFORMATION_STEP_VERSION,
115+
id: nanoid(),
116+
type: 'prompt_transform',
117+
'prompt_transform.inference.provider': 'Google',
118+
'prompt_transform.inference.provider.OpenAI.model': 'gpt-4o',
119+
'prompt_transform.inference.provider.Groq.model': 'llama-3.3-70b-versatile',
120+
'prompt_transform.inference.provider.Anthropic.model': 'claude-sonnet-4-0',
121+
'prompt_transform.inference.provider.Google.model': 'gemini-2.5-flash',
122+
'prompt_transform.inference.provider.OpenRouter.model':
123+
'mistralai/mixtral-8x7b',
124+
// Empty strings for Custom provider - user must configure when switching to Custom
125+
// baseUrl falls back to global setting in transformer.ts
126+
'prompt_transform.inference.provider.Custom.model': '',
127+
'prompt_transform.inference.provider.Custom.baseUrl': '',
128+
129+
'prompt_transform.systemPromptTemplate': '',
130+
'prompt_transform.userPromptTemplate': '',
131+
132+
'find_replace.findText': '',
133+
'find_replace.replaceText': '',
134+
'find_replace.useRegex': false,
135+
};
136+
}
Lines changed: 19 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,54 @@
11
import { type } from 'arktype';
22
import { nanoid } from 'nanoid/non-secure';
3-
import { TRANSFORMATION_STEP_TYPES } from '$lib/constants/database';
43
import {
5-
ANTHROPIC_INFERENCE_MODELS,
6-
GOOGLE_INFERENCE_MODELS,
7-
GROQ_INFERENCE_MODELS,
8-
INFERENCE_PROVIDER_IDS,
9-
OPENAI_INFERENCE_MODELS,
10-
} from '$lib/constants/inference';
11-
12-
/**
13-
* The current version of the TransformationStep schema.
14-
* Increment this when adding new fields or making breaking changes.
15-
*/
16-
const CURRENT_TRANSFORMATION_STEP_VERSION = 2 as const;
4+
TransformationStep,
5+
TransformationStepV1,
6+
TransformationStepV2,
7+
} from './transformation-steps.js';
178

189
// ============================================================================
1910
// VERSION 1 (FROZEN)
2011
// ============================================================================
2112

22-
/**
23-
* V1: Original schema without Custom provider fields.
24-
* Old data has no version field, so we default to 1.
25-
*
26-
* FROZEN: Do not modify. This represents the historical V1 schema.
27-
*/
28-
const TransformationStepV1 = type({
29-
id: 'string',
30-
type: type.enumerated(...TRANSFORMATION_STEP_TYPES),
31-
'prompt_transform.inference.provider': type.enumerated(
32-
...INFERENCE_PROVIDER_IDS,
33-
),
34-
'prompt_transform.inference.provider.OpenAI.model': type.enumerated(
35-
...OPENAI_INFERENCE_MODELS,
36-
),
37-
'prompt_transform.inference.provider.Groq.model': type.enumerated(
38-
...GROQ_INFERENCE_MODELS,
39-
),
40-
'prompt_transform.inference.provider.Anthropic.model': type.enumerated(
41-
...ANTHROPIC_INFERENCE_MODELS,
42-
),
43-
'prompt_transform.inference.provider.Google.model': type.enumerated(
44-
...GOOGLE_INFERENCE_MODELS,
45-
),
46-
// OpenRouter model is a free string (user can enter any model)
47-
'prompt_transform.inference.provider.OpenRouter.model': 'string',
48-
'prompt_transform.systemPromptTemplate': 'string',
49-
'prompt_transform.userPromptTemplate': 'string',
50-
'find_replace.findText': 'string',
51-
'find_replace.replaceText': 'string',
52-
'find_replace.useRegex': 'boolean',
53-
version: '1 = 1',
54-
});
55-
56-
export type TransformationStepV1 = typeof TransformationStepV1.infer;
57-
5813
/**
5914
* Transformation type containing V1 steps (before Custom provider fields).
6015
* Used only for typing old data during Dexie migration in web.ts.
6116
*
6217
* Note: The Transformation fields themselves are unchanged; only the step
6318
* schema differs between "V1" and "V2".
6419
*/
65-
export type TransformationV1 = {
66-
id: string;
67-
title: string;
68-
description: string;
69-
createdAt: string;
70-
updatedAt: string;
71-
steps: TransformationStepV1[];
72-
};
20+
const TransformationV1 = type({
21+
id: 'string',
22+
title: 'string',
23+
description: 'string',
24+
createdAt: 'string',
25+
updatedAt: 'string',
26+
steps: [TransformationStepV1, '[]'],
27+
});
28+
29+
export type TransformationV1 = typeof TransformationV1.infer;
7330

7431
// ============================================================================
7532
// VERSION 2 (CURRENT)
7633
// ============================================================================
7734

78-
/**
79-
* V2: Added Custom provider fields for local LLM endpoints.
80-
* Extends V1 with:
81-
* - Custom.model: Model name for custom endpoints
82-
* - Custom.baseUrl: Per-step base URL (falls back to global setting)
83-
*
84-
* CURRENT VERSION: This is the latest schema.
85-
*/
86-
const TransformationStepV2 = TransformationStepV1.merge({
87-
version: '2',
88-
/** Custom provider for local LLM endpoints (Ollama, LM Studio, llama.cpp, etc.) */
89-
'prompt_transform.inference.provider.Custom.model': 'string',
90-
/**
91-
* Per-step base URL for custom endpoints. Allows different steps to use
92-
* different local services (e.g., Ollama on :11434, LM Studio on :1234).
93-
* Falls back to global `completion.Custom.baseUrl` setting if empty.
94-
*/
95-
'prompt_transform.inference.provider.Custom.baseUrl': 'string',
96-
});
97-
98-
export type TransformationStepV2 = typeof TransformationStepV2.infer;
99-
10035
/**
10136
* Current Transformation schema with V2 steps.
37+
* Extends V1 by using V2 steps.
10238
*
10339
* The Transformation container fields (id, title, description, createdAt, updatedAt)
10440
* have not changed since V1. Only TransformationStep has versioning.
10541
*/
106-
const TransformationV2 = type({
107-
id: 'string',
108-
title: 'string',
109-
description: 'string',
110-
createdAt: 'string',
111-
updatedAt: 'string',
42+
const TransformationV2 = TransformationV1.merge({
11243
steps: [TransformationStepV2, '[]'],
11344
});
11445

11546
export type TransformationV2 = typeof TransformationV2.infer;
11647

11748
// ============================================================================
118-
// MIGRATING VALIDATORS
119-
// ============================================================================
120-
// These accept any version and migrate to the latest (V2).
121-
// Use these when reading data that might be from an older schema version.
49+
// MIGRATING VALIDATOR
12250
// ============================================================================
12351

124-
/**
125-
* TransformationStep validator with automatic migration.
126-
* Accepts V1 or V2 and always outputs V2.
127-
*/
128-
export const TransformationStep = TransformationStepV1.or(
129-
TransformationStepV2,
130-
).pipe((step): TransformationStepV2 => {
131-
if (step.version === 1) {
132-
return {
133-
...step,
134-
version: 2,
135-
'prompt_transform.inference.provider.Custom.model': '',
136-
'prompt_transform.inference.provider.Custom.baseUrl': '',
137-
};
138-
}
139-
return step;
140-
});
141-
142-
export type TransformationStep = TransformationStepV2;
143-
14452
/**
14553
* Transformation validator with automatic step migration.
14654
* Accepts transformations with V1 or V2 steps and migrates all steps to V2.
@@ -153,10 +61,10 @@ export const Transformation = TransformationV2.merge({
15361
export type Transformation = TransformationV2;
15462

15563
// ============================================================================
156-
// FACTORY FUNCTIONS
64+
// FACTORY FUNCTION
15765
// ============================================================================
15866

159-
export function generateDefaultTransformation(): TransformationV2 {
67+
export function generateDefaultTransformation(): Transformation {
16068
const now = new Date().toISOString();
16169
return {
16270
id: nanoid(),
@@ -167,29 +75,3 @@ export function generateDefaultTransformation(): TransformationV2 {
16775
updatedAt: now,
16876
};
16977
}
170-
171-
export function generateDefaultTransformationStep(): TransformationStepV2 {
172-
return {
173-
version: CURRENT_TRANSFORMATION_STEP_VERSION,
174-
id: nanoid(),
175-
type: 'prompt_transform',
176-
'prompt_transform.inference.provider': 'Google',
177-
'prompt_transform.inference.provider.OpenAI.model': 'gpt-4o',
178-
'prompt_transform.inference.provider.Groq.model': 'llama-3.3-70b-versatile',
179-
'prompt_transform.inference.provider.Anthropic.model': 'claude-sonnet-4-0',
180-
'prompt_transform.inference.provider.Google.model': 'gemini-2.5-flash',
181-
'prompt_transform.inference.provider.OpenRouter.model':
182-
'mistralai/mixtral-8x7b',
183-
// Empty strings for Custom provider - user must configure when switching to Custom
184-
// baseUrl falls back to global setting in transformer.ts
185-
'prompt_transform.inference.provider.Custom.model': '',
186-
'prompt_transform.inference.provider.Custom.baseUrl': '',
187-
188-
'prompt_transform.systemPromptTemplate': '',
189-
'prompt_transform.userPromptTemplate': '',
190-
191-
'find_replace.findText': '',
192-
'find_replace.replaceText': '',
193-
'find_replace.useRegex': false,
194-
};
195-
}

0 commit comments

Comments
 (0)