Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions PREVENT_CONFLICTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ This document outlines strategies to minimize merge conflicts and improve produc

### Immediate Actions
1. [x] **Refactor Plugin Registration**: Modify `PluginManager` to accept a list of plugins without hardcoding defaults in the class itself. Move default plugin logic to a separate `DefaultPlugins` module. (Completed 2025-02-18)
2. **Harden Types**: systematically review `src/core/interfaces.ts` and replace `any` with generic types or `unknown` with validation. specifically target `RequestContext`, `DataSourceProvider`, and `DataActionProvider`.
3. **Decompose `dataprompt.ts`**: Extract `createDefaultGenkit` and `loadUserGenkitInstance` into a separate `GenkitFactory` module.
2. [x] **Harden Types**: systematically review `src/core/interfaces.ts` and replace `any` with generic types or `unknown` with validation. specifically target `RequestContext`, `DataSourceProvider`, and `DataActionProvider`. (Completed 2025-02-22)
3. [x] **Decompose `dataprompt.ts`**: Extract `createDefaultGenkit` and `loadUserGenkitInstance` into a separate `GenkitFactory` module. (Completed 2025-02-22)

### Long-term Strategy
1. **Adopt "Open for Extension, Closed for Modification"**: Design core classes to accept extensions (plugins, commands, routes) without requiring modification to the class source code.
Expand Down
32 changes: 16 additions & 16 deletions src/core/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export const RequestContextSchema = z.object({
z.union([z.string(), z.array(z.string())])
).optional(),
body: z.object({
json: z.any().optional(),
form: z.record(z.string(), z.any()).optional(),
json: z.unknown().optional(),
form: z.record(z.string(), z.unknown()).optional(),
text: z.string().optional()
}).optional(),
requestId: z.string().optional()
Expand All @@ -28,48 +28,48 @@ export type RequestContext = z.infer<typeof RequestContextSchema>;

export interface DatapromptPlugin<Schema extends z.AnyZodObject = z.AnyZodObject> {
name: string;
createDataSource?(): DataSourceProvider;
createDataAction?(): DataActionProvider;
createDataSource?(): DataSourceProvider<unknown>;
createDataAction?(): DataActionProvider<unknown>;
createTrigger?(): TriggerProvider;
provideSecrets?(): {
secrets: Partial<z.infer<Schema>>;
schema?: Schema;
} | undefined;
provideGenkitPlugins?(): any[];
provideGenkitPlugins?(): unknown[];
}

export type FetchDataParams = {
export type FetchDataParams<Config = unknown> = {
request: RequestContext;
config: any;
config: Config;
file: DatapromptFile;
}

export interface DataSourceProvider {
export interface DataSourceProvider<Config = unknown> {
name: string;
fetchData(params: FetchDataParams): Promise<Record<string, any> | string>;
fetchData(params: FetchDataParams<Config>): Promise<unknown>;
}

export type ExecuteParams = {
export type ExecuteParams<Config = unknown> = {
request: RequestContext;
config: any;
promptSources: Record<string, any>;
config: Config;
promptSources: Record<string, unknown>;
file: DatapromptFile;
}

export interface DataActionProvider {
export interface DataActionProvider<Config = unknown> {
name: string;
execute(params: ExecuteParams): Promise<void>;
execute(params: ExecuteParams<Config>): Promise<void>;
}

export interface TriggerConfig {
type: string;
config: any;
config: unknown;
}

export interface Trigger {
create(
route: DatapromptRoute,
config: any,
config: unknown,
): ScheduledTask;
}

Expand Down
10 changes: 5 additions & 5 deletions src/core/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function createGenkitPrompt(options: {
const promptInputSchema = z.object({
...Object.fromEntries(
Object.entries(sources).flatMap(([sourceName, sourceConfig]) => {
return Object.keys(sourceConfig).map(propertyName => [propertyName, z.any()])
return Object.keys(sourceConfig).map(propertyName => [propertyName, z.unknown()])
})
),
request: RequestContextSchema,
Expand Down Expand Up @@ -63,7 +63,7 @@ export class Prompt {
* Orchestrates the full execution of the prompt.
* @param request The incoming request context.
*/
async execute(request: RequestContext): Promise<any> {
async execute(request: RequestContext): Promise<unknown> {
// 1. Fetch all necessary data sources.
const promptSources = await this.#fetchData(request);

Expand All @@ -83,9 +83,9 @@ export class Prompt {
/**
* Fetches data from all sources defined in the prompt's frontmatter.
*/
async #fetchData(request: RequestContext): Promise<Record<string, any>> {
async #fetchData(request: RequestContext): Promise<Record<string, unknown>> {
const sources = this.#flowDef.data?.prompt?.sources || {};
const promptSources: Record<string, any> = {};
const promptSources: Record<string, unknown> = {};

for (const [sourceName, sourceConfig] of Object.entries(sources)) {
const sourceProvider = this.#pluginManager.getDataSource(sourceName);
Expand All @@ -107,7 +107,7 @@ export class Prompt {
/**
* Executes all result actions defined in the prompt's frontmatter.
*/
async #executeActions(request: RequestContext, promptSources: Record<string, any>, result: { output: any }): Promise<void> {
async #executeActions(request: RequestContext, promptSources: Record<string, unknown>, result: { output: unknown }): Promise<void> {
const resultActions = this.#flowDef.data?.prompt?.result || {};
for (const [actionName, actionConfig] of Object.entries(resultActions)) {
await this.#ai.run(`ResultAction: ${actionName}`, async () => {
Expand Down
12 changes: 5 additions & 7 deletions src/plugins/fetch/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RequestContext } from '../../core/interfaces.js';
import { DataSourceProvider, DatapromptPlugin } from '../../core/interfaces.js';
import { DataSourceProvider, DatapromptPlugin, FetchDataParams } from '../../core/interfaces.js';

export interface FetchConfig {
url: string;
Expand All @@ -10,14 +10,12 @@ export function fetchPlugin(): DatapromptPlugin {
const name = 'fetch'
return {
name,
createDataSource(): DataSourceProvider {
createDataSource(): DataSourceProvider<unknown> {
return {
name,
fetchData(params: {
request: RequestContext,
config: string | FetchConfig
}) {
return fetchData(params)
fetchData(params: FetchDataParams<unknown>) {
const config = params.config as string | FetchConfig;
return fetchData({ ...params, config });
}
}
}
Expand Down
17 changes: 10 additions & 7 deletions src/plugins/firebase/firestore/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { z } from 'genkit';
import { DataActionProvider, DataSourceProvider, DatapromptPlugin } from '../../../core/interfaces.js';
import { DataActionProvider, DataSourceProvider, DatapromptPlugin, FetchDataParams, ExecuteParams } from '../../../core/interfaces.js';
import { getFirebaseApp } from '../app.js'
import { getFirestore } from 'firebase-admin/firestore';
import { fetchData } from './source.js'
import { execute } from './actions.js'
import { FirebasePluginConfig } from '../types.js';
import { FirestoreSourceConfig, FirestoreBatchConfig } from './types.js';

const FirestorePluginSecrets = z.object({
GOOGLE_APPLICATION_CREDENTIALS: z.string().optional()
Expand All @@ -20,19 +21,21 @@ export function firestorePlugin(

return {
name,
createDataSource(): DataSourceProvider {
createDataSource(): DataSourceProvider<unknown> {
return {
name,
fetchData(params) {
return fetchData({ db, ...params })
fetchData(params: FetchDataParams<unknown>) {
const config = params.config as FirestoreSourceConfig;
return fetchData({ db, ...params, config })
}
}
},
createDataAction(): DataActionProvider {
createDataAction(): DataActionProvider<unknown> {
return {
name,
execute(params): Promise<void> {
return execute({ db, ...params })
execute(params: ExecuteParams<unknown>): Promise<void> {
const config = params.config as FirestoreBatchConfig;
return execute({ db, ...params, config })
}
}
},
Expand Down
27 changes: 10 additions & 17 deletions src/plugins/fs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {
DataSourceProvider,
DataActionProvider,
DatapromptPlugin,
RequestContext,
DatapromptFile,
FetchDataParams,
ExecuteParams,
} from '../../core/interfaces.js';

export function fsPlugin(pluginConfig: FileSystemPluginConfig = {}): DatapromptPlugin {
Expand All @@ -19,28 +19,21 @@ export function fsPlugin(pluginConfig: FileSystemPluginConfig = {}): DatapromptP

return {
name,
createDataSource(): DataSourceProvider {
createDataSource(): DataSourceProvider<unknown> {
return {
name,
async fetchData(params: {
request: RequestContext;
config: string | FileSystemReadConfig;
file: DatapromptFile;
}): Promise<Record<string, any> | Buffer | string> {
return fetchData(params, sandboxPath);
async fetchData(params: FetchDataParams<unknown>): Promise<Record<string, any> | Buffer | string> {
const config = params.config as string | FileSystemReadConfig;
return fetchData({ ...params, config }, sandboxPath);
},
};
},
createDataAction(): DataActionProvider {
createDataAction(): DataActionProvider<unknown> {
return {
name,
async execute(params: {
request: RequestContext;
config: Record<WriteOperationType, FileSystemWriteConfig | FileSystemWriteConfig[]>;
promptSources: Record<string, any>;
file: DatapromptFile;
}): Promise<void> {
return execute(params, sandboxPath)
async execute(params: ExecuteParams<unknown>): Promise<void> {
const config = params.config as Record<WriteOperationType, FileSystemWriteConfig | FileSystemWriteConfig[]>;
return execute({ ...params, config }, sandboxPath)
},
};
},
Expand Down
3 changes: 2 additions & 1 deletion src/plugins/scheduler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ class DevScheduleTrigger implements Trigger {

create(
route: DatapromptRoute,
cronExpression: string,
config: unknown,
): ScheduledTask {
const cronExpression = config as string;
const name = `flow-${route.flowDef.name}`

if (!this.cron.validate(cronExpression)) {
Expand Down