diff --git a/genkit-tools/common/jest.config.ts b/genkit-tools/common/jest.config.ts index cd67fb1658..59bf08de63 100644 --- a/genkit-tools/common/jest.config.ts +++ b/genkit-tools/common/jest.config.ts @@ -41,6 +41,10 @@ const config: Config = { // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation transformIgnorePatterns: ['/node_modules/'], + + moduleNameMapper: { + '^(\\.{1,2}/.*)\\.js$': '$1', + }, }; export default config; diff --git a/genkit-tools/common/src/types/action.ts b/genkit-tools/common/src/types/action.ts index f75d9b370f..c1cf2a9f62 100644 --- a/genkit-tools/common/src/types/action.ts +++ b/genkit-tools/common/src/types/action.ts @@ -14,21 +14,23 @@ * limitations under the License. */ -import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; -import { z } from 'zod'; - -extendZodWithOpenApi(z); +import * as z from 'zod'; // `z.any()` generates `{ nullable: true }` with no type set which is not valid OpenAPI spec. // There's no way around this without defining a custom type. `z.array()` needs an inner type which runs into the same issue. -export const SingularAnySchema = z - .union([z.string(), z.number(), z.bigint(), z.boolean(), z.object({})]) - .openapi('SingularAny'); +export const SingularAnySchema = z.union([ + z.string(), + z.number(), + z.bigint(), + z.boolean(), + z.object({}), +]); -export const CustomAnySchema = z - .union([SingularAnySchema, z.array(SingularAnySchema)]) - .openapi('CustomAny'); +export const CustomAnySchema = z.union([ + SingularAnySchema, + z.array(SingularAnySchema), +]); // TODO: Figure out a way to do z.custom() and have it generate a nullable object correctly. export const JSONSchema7Schema = z @@ -36,25 +38,22 @@ export const JSONSchema7Schema = z .describe( 'A JSON Schema Draft 7 (http://json-schema.org/draft-07/schema) object.' ) - .nullish() - .openapi('JSONSchema7'); + .nullish(); -export const ActionSchema = z - .object({ - key: z.string().describe('Action key consisting of action type and ID.'), - name: z.string().describe('Action name.'), - description: z - .string() - .describe('A description of what the action does.') - .nullish(), - inputSchema: JSONSchema7Schema, - outputSchema: JSONSchema7Schema, - metadata: z - .record(z.string(), CustomAnySchema) - .describe('Metadata about the action (e.g. supported model features).') - .nullish(), - }) - .openapi('Action'); +export const ActionSchema = z.object({ + key: z.string().describe('Action key consisting of action type and ID.'), + name: z.string().describe('Action name.'), + description: z + .string() + .describe('A description of what the action does.') + .nullish(), + inputSchema: JSONSchema7Schema, + outputSchema: JSONSchema7Schema, + metadata: z + .record(z.string(), CustomAnySchema) + .describe('Metadata about the action (e.g. supported model features).') + .nullish(), +}); export type Action = z.infer; diff --git a/genkit-tools/common/src/types/document.ts b/genkit-tools/common/src/types/document.ts index c21fbd9362..89683b53d8 100644 --- a/genkit-tools/common/src/types/document.ts +++ b/genkit-tools/common/src/types/document.ts @@ -28,6 +28,7 @@ const EmptyPartSchema = z.object({ metadata: z.record(z.unknown()).optional(), custom: z.record(z.unknown()).optional(), reasoning: z.never().optional(), + resource: z.never().optional(), }); /** @@ -147,6 +148,15 @@ export const CustomPartSchema = EmptyPartSchema.extend({ */ export type CustomPart = z.infer; +/** + * Zod schema of a resource part. + */ +export const ResourcePartSchema = EmptyPartSchema.extend({ + resource: z.object({ + uri: z.string(), + }), +}); + // Disclaimer: genkit/js/ai/document.ts defines the following schema, type pair // as PartSchema and Part, respectively. genkit-tools cannot retain those names // due to it clashing with similar schema in model.ts, and genkit-tools diff --git a/genkit-tools/common/src/types/model.ts b/genkit-tools/common/src/types/model.ts index 57079ddfc4..fa4f8f2d46 100644 --- a/genkit-tools/common/src/types/model.ts +++ b/genkit-tools/common/src/types/model.ts @@ -20,6 +20,7 @@ import { DocumentDataSchema, MediaPartSchema, ReasoningPartSchema, + ResourcePartSchema, TextPartSchema, ToolRequestPartSchema, ToolResponsePartSchema, @@ -29,7 +30,7 @@ import { type TextPart, type ToolRequestPart, type ToolResponsePart, -} from './document'; +} from './document.js'; export { CustomPartSchema, DataPartSchema, @@ -77,6 +78,7 @@ export const PartSchema = z.union([ DataPartSchema, CustomPartSchema, ReasoningPartSchema, + ResourcePartSchema, ]); /** @@ -183,18 +185,59 @@ export const ToolDefinitionSchema = z.object({ */ export type ToolDefinition = z.infer; +/** + * Configuration parameter descriptions. + */ +export const GenerationCommonConfigDescriptions = { + temperature: + 'Controls the degree of randomness in token selection. A lower value is ' + + 'good for a more predictable response. A higher value leads to more ' + + 'diverse or unexpected results.', + maxOutputTokens: 'The maximum number of tokens to include in the response.', + topK: 'The maximum number of tokens to consider when sampling.', + topP: + 'Decides how many possible words to consider. A higher value means ' + + 'that the model looks at more possible words, even the less likely ' + + 'ones, which makes the generated text more diverse.', +}; + /** * Zod schema of a common config object. */ -export const GenerationCommonConfigSchema = z.object({ - /** A specific version of a model family, e.g. `gemini-1.0-pro-001` for the `gemini-1.0-pro` family. */ - version: z.string().optional(), - temperature: z.number().optional(), - maxOutputTokens: z.number().optional(), - topK: z.number().optional(), - topP: z.number().optional(), - stopSequences: z.array(z.string()).optional(), -}); +export const GenerationCommonConfigSchema = z + .object({ + version: z + .string() + .describe( + 'A specific version of a model family, e.g. `gemini-2.0-flash` ' + + 'for the `googleai` family.' + ) + .optional(), + temperature: z + .number() + .describe(GenerationCommonConfigDescriptions.temperature) + .optional(), + maxOutputTokens: z + .number() + .describe(GenerationCommonConfigDescriptions.maxOutputTokens) + .optional(), + topK: z + .number() + .describe(GenerationCommonConfigDescriptions.topK) + .optional(), + topP: z + .number() + .describe(GenerationCommonConfigDescriptions.topP) + .optional(), + stopSequences: z + .array(z.string()) + .max(5) + .describe( + 'Set of character sequences (up to 5) that will stop output generation.' + ) + .optional(), + }) + .passthrough(); /** * Common config object. diff --git a/genkit-tools/common/src/types/trace.ts b/genkit-tools/common/src/types/trace.ts index 15587b6d52..be2593e063 100644 --- a/genkit-tools/common/src/types/trace.ts +++ b/genkit-tools/common/src/types/trace.ts @@ -14,11 +14,8 @@ * limitations under the License. */ -import { extendZodWithOpenApi } from '@asteasolutions/zod-to-openapi'; import * as z from 'zod'; -extendZodWithOpenApi(z); - // NOTE: Keep this file in sync with js/core/src/tracing/types.ts! // Eventually tools will be source of truth for these types (by generating a // JSON schema) but until then this file must be manually kept in sync @@ -108,48 +105,44 @@ export const InstrumentationLibrarySchema = z.object({ /** * Zod schema for span data. */ -export const SpanDataSchema = z - .object({ - spanId: z.string(), - traceId: z.string(), - parentSpanId: z.string().optional(), - startTime: z.number(), - endTime: z.number(), - attributes: z.record(z.string(), z.unknown()), - displayName: z.string(), - links: z.array(LinkSchema).optional(), - instrumentationLibrary: InstrumentationLibrarySchema, - spanKind: z.string(), - sameProcessAsParentSpan: z.object({ value: z.boolean() }).optional(), - status: SpanStatusSchema.optional(), - timeEvents: z - .object({ - timeEvent: z.array(TimeEventSchema).optional(), - }) - .optional(), - truncated: z.boolean().optional(), - }) - .openapi('SpanData'); +export const SpanDataSchema = z.object({ + spanId: z.string(), + traceId: z.string(), + parentSpanId: z.string().optional(), + startTime: z.number(), + endTime: z.number(), + attributes: z.record(z.string(), z.unknown()), + displayName: z.string(), + links: z.array(LinkSchema).optional(), + instrumentationLibrary: InstrumentationLibrarySchema, + spanKind: z.string(), + sameProcessAsParentSpan: z.object({ value: z.boolean() }).optional(), + status: SpanStatusSchema.optional(), + timeEvents: z + .object({ + timeEvent: z.array(TimeEventSchema).optional(), + }) + .optional(), + truncated: z.boolean().optional(), +}); export type SpanData = z.infer; /** * Zod schema for trace metadata. */ -export const TraceDataSchema = z - .object({ - traceId: z.string(), - displayName: z.string().optional(), - startTime: z - .number() - .optional() - .describe('trace start time in milliseconds since the epoch'), - endTime: z - .number() - .optional() - .describe('end time in milliseconds since the epoch'), - spans: z.record(z.string(), SpanDataSchema), - }) - .openapi('TraceData'); +export const TraceDataSchema = z.object({ + traceId: z.string(), + displayName: z.string().optional(), + startTime: z + .number() + .optional() + .describe('trace start time in milliseconds since the epoch'), + endTime: z + .number() + .optional() + .describe('end time in milliseconds since the epoch'), + spans: z.record(z.string(), SpanDataSchema), +}); export type TraceData = z.infer; export const NestedSpanDataSchema = SpanDataSchema.extend({ diff --git a/genkit-tools/genkit-schema.json b/genkit-tools/genkit-schema.json index bc637c7551..7f1c11963b 100644 --- a/genkit-tools/genkit-schema.json +++ b/genkit-tools/genkit-schema.json @@ -27,6 +27,9 @@ }, "reasoning": { "not": {} + }, + "resource": { + "not": {} } }, "required": [ @@ -59,6 +62,9 @@ }, "reasoning": { "$ref": "#/$defs/CustomPart/properties/reasoning" + }, + "resource": { + "$ref": "#/$defs/CustomPart/properties/resource" } }, "additionalProperties": false @@ -118,6 +124,9 @@ }, "reasoning": { "$ref": "#/$defs/CustomPart/properties/reasoning" + }, + "resource": { + "$ref": "#/$defs/CustomPart/properties/resource" } }, "required": [ @@ -166,6 +175,9 @@ }, "reasoning": { "type": "string" + }, + "resource": { + "$ref": "#/$defs/CustomPart/properties/resource" } }, "required": [ @@ -173,6 +185,51 @@ ], "additionalProperties": false }, + "ResourcePart": { + "type": "object", + "properties": { + "text": { + "$ref": "#/$defs/CustomPart/properties/text" + }, + "media": { + "$ref": "#/$defs/CustomPart/properties/media" + }, + "toolRequest": { + "$ref": "#/$defs/CustomPart/properties/toolRequest" + }, + "toolResponse": { + "$ref": "#/$defs/CustomPart/properties/toolResponse" + }, + "data": { + "$ref": "#/$defs/CustomPart/properties/data" + }, + "metadata": { + "$ref": "#/$defs/CustomPart/properties/metadata" + }, + "custom": { + "$ref": "#/$defs/DataPart/properties/custom" + }, + "reasoning": { + "$ref": "#/$defs/CustomPart/properties/reasoning" + }, + "resource": { + "type": "object", + "properties": { + "uri": { + "type": "string" + } + }, + "required": [ + "uri" + ], + "additionalProperties": false + } + }, + "required": [ + "resource" + ], + "additionalProperties": false + }, "TextPart": { "type": "object", "properties": { @@ -199,6 +256,9 @@ }, "reasoning": { "$ref": "#/$defs/CustomPart/properties/reasoning" + }, + "resource": { + "$ref": "#/$defs/CustomPart/properties/resource" } }, "required": [ @@ -232,6 +292,9 @@ }, "reasoning": { "$ref": "#/$defs/CustomPart/properties/reasoning" + }, + "resource": { + "$ref": "#/$defs/CustomPart/properties/resource" } }, "required": [ @@ -281,6 +344,9 @@ }, "reasoning": { "$ref": "#/$defs/CustomPart/properties/reasoning" + }, + "resource": { + "$ref": "#/$defs/CustomPart/properties/resource" } }, "required": [ @@ -805,28 +871,35 @@ "type": "object", "properties": { "version": { - "type": "string" + "type": "string", + "description": "A specific version of a model family, e.g. `gemini-2.0-flash` for the `googleai` family." }, "temperature": { - "type": "number" + "type": "number", + "description": "Controls the degree of randomness in token selection. A lower value is good for a more predictable response. A higher value leads to more diverse or unexpected results." }, "maxOutputTokens": { - "type": "number" + "type": "number", + "description": "The maximum number of tokens to include in the response." }, "topK": { - "type": "number" + "type": "number", + "description": "The maximum number of tokens to consider when sampling." }, "topP": { - "type": "number" + "type": "number", + "description": "Decides how many possible words to consider. A higher value means that the model looks at more possible words, even the less likely ones, which makes the generated text more diverse." }, "stopSequences": { "type": "array", "items": { "type": "string" - } + }, + "maxItems": 5, + "description": "Set of character sequences (up to 5) that will stop output generation." } }, - "additionalProperties": false + "additionalProperties": true }, "GenerationUsage": { "type": "object", @@ -1138,6 +1211,9 @@ }, { "$ref": "#/$defs/ReasoningPart" + }, + { + "$ref": "#/$defs/ResourcePart" } ] }, diff --git a/js/package.json b/js/package.json index 7b3829476a..cd3d362dbe 100644 --- a/js/package.json +++ b/js/package.json @@ -4,7 +4,7 @@ "preinstall": "npx only-allow pnpm", "build": "pnpm install && pnpm build:libs && pnpm build:docsnippets && pnpm build:testapps", "build:libs": "pnpm build:core && pnpm build:genkit && pnpm build:noncore ", - "build:core": "pnpm -r --workspace-concurrency 1 -F core -F ai -F flow build", + "build:core": "pnpm -r --workspace-concurrency 1 -F core -F shared -F ai -F flow build", "build:genkit": "pnpm -F genkit build", "build:noncore": "pnpm -r --workspace-concurrency -1 -F \"./plugins/**\" build", "build:testapps": "pnpm -r --workspace-concurrency -1 -F \"./testapps/**\" build", diff --git a/js/pnpm-lock.yaml b/js/pnpm-lock.yaml index 1d95cb2a17..111e8be835 100644 --- a/js/pnpm-lock.yaml +++ b/js/pnpm-lock.yaml @@ -972,6 +972,31 @@ importers: specifier: '>=12.2' version: 13.4.0(encoding@0.1.13) + shared: + dependencies: + zod: + specifier: ^3.23.8 + version: 3.25.67 + devDependencies: + genversion: + specifier: ^3.2.0 + version: 3.2.0 + npm-run-all: + specifier: ^4.1.5 + version: 4.1.5 + rimraf: + specifier: ^6.0.1 + version: 6.0.1 + tsup: + specifier: ^8.3.5 + version: 8.5.0(postcss@8.4.47)(tsx@4.20.3)(typescript@4.9.5)(yaml@2.8.0) + tsx: + specifier: ^4.19.2 + version: 4.20.3 + typescript: + specifier: ^4.9.0 + version: 4.9.5 + testapps/basic-gemini: dependencies: '@genkit-ai/firebase': @@ -1004,7 +1029,7 @@ importers: version: link:../../plugins/compat-oai '@genkit-ai/express': specifier: ^1.1.0 - version: 1.12.0(@genkit-ai/core@1.14.1)(express@5.1.0)(genkit@genkit) + version: 1.12.0(@genkit-ai/core@1.15.1)(express@5.1.0)(genkit@genkit) genkit: specifier: workspace:* version: link:../../genkit @@ -1639,7 +1664,7 @@ importers: version: link:../../plugins/ollama genkitx-openai: specifier: ^0.10.1 - version: 0.10.1(@genkit-ai/ai@1.14.1)(@genkit-ai/core@1.14.1) + version: 0.10.1(@genkit-ai/ai@1.15.1)(@genkit-ai/core@1.15.1) devDependencies: rimraf: specifier: ^6.0.1 @@ -2640,11 +2665,11 @@ packages: '@firebase/webchannel-wrapper@1.0.3': resolution: {integrity: sha512-2xCRM9q9FlzGZCdgDMJwc0gyUkWFtkosy7Xxr6sFgQwn+wMNIWd7xIvYNauU1r64B5L5rsGKy/n9TKJ0aAFeqQ==} - '@genkit-ai/ai@1.14.1': - resolution: {integrity: sha512-/Wuy09ZNvoD4f85SZDCciiuf0fL6xnkHM2Wvkw9cScJp9pjaCuy+XvfX0y4OZ5B6cvWTUnvo35bLv6CcJFwkOQ==} + '@genkit-ai/ai@1.15.1': + resolution: {integrity: sha512-9rFT2NZmm5B9Mnw2jYdoyH7KQaEHgw8U4qVdyJoxdosr+GqEf6HbsjFTfDg6B9tkBXQjtIIHsDSKr5Ylbzhlbw==} - '@genkit-ai/core@1.14.1': - resolution: {integrity: sha512-MzN9UeI5x43g0HRSFjAvZUGQKi6hgRdMUDVIp3Eh/FenfqpKkKdHIJiY+N5U5XiA7hb2A2xIhCu5pUiHMGlyoA==} + '@genkit-ai/core@1.15.1': + resolution: {integrity: sha512-jSnhU0CO0fJHdxJe+/BU6tj7Zwyoh5jXBXeHhrIn2cae8LeowjUZCP1JglbwHpAtWNeX9UFKHLhubZoAVZ28Iw==} '@genkit-ai/express@1.12.0': resolution: {integrity: sha512-QAxSS07dX5ovSfsUB4s90KaDnv4zg1wnoxCZCa+jBsYUyv9NvCCTsOk25xAQgGxc7xi3+MD+3AsPier5oZILIg==} @@ -8409,9 +8434,9 @@ snapshots: '@firebase/webchannel-wrapper@1.0.3': {} - '@genkit-ai/ai@1.14.1': + '@genkit-ai/ai@1.15.1': dependencies: - '@genkit-ai/core': 1.14.1 + '@genkit-ai/core': 1.15.1 '@opentelemetry/api': 1.9.0 '@types/node': 20.19.1 colorette: 2.0.20 @@ -8424,7 +8449,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/core@1.14.1': + '@genkit-ai/core@1.15.1': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) @@ -8447,9 +8472,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@genkit-ai/express@1.12.0(@genkit-ai/core@1.14.1)(express@5.1.0)(genkit@genkit)': + '@genkit-ai/express@1.12.0(@genkit-ai/core@1.15.1)(express@5.1.0)(genkit@genkit)': dependencies: - '@genkit-ai/core': 1.14.1 + '@genkit-ai/core': 1.15.1 body-parser: 1.20.3 cors: 2.8.5 express: 5.1.0 @@ -11413,10 +11438,10 @@ snapshots: - encoding - supports-color - genkitx-openai@0.10.1(@genkit-ai/ai@1.14.1)(@genkit-ai/core@1.14.1): + genkitx-openai@0.10.1(@genkit-ai/ai@1.15.1)(@genkit-ai/core@1.15.1): dependencies: - '@genkit-ai/ai': 1.14.1 - '@genkit-ai/core': 1.14.1 + '@genkit-ai/ai': 1.15.1 + '@genkit-ai/core': 1.15.1 openai: 4.104.0(encoding@0.1.13)(zod@3.25.67) zod: 3.25.67 transitivePeerDependencies: diff --git a/js/shared/.gitignore b/js/shared/.gitignore new file mode 100644 index 0000000000..29f7d2e934 --- /dev/null +++ b/js/shared/.gitignore @@ -0,0 +1 @@ +src/__codegen \ No newline at end of file diff --git a/js/shared/.npmignore b/js/shared/.npmignore new file mode 100644 index 0000000000..5ea8bc610d --- /dev/null +++ b/js/shared/.npmignore @@ -0,0 +1,3 @@ +node_modules +tsconfig.json +tsup.config.ts \ No newline at end of file diff --git a/js/shared/LICENSE b/js/shared/LICENSE new file mode 100644 index 0000000000..26a870243c --- /dev/null +++ b/js/shared/LICENSE @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + \ No newline at end of file diff --git a/js/shared/README.md b/js/shared/README.md new file mode 100644 index 0000000000..301023ddbc --- /dev/null +++ b/js/shared/README.md @@ -0,0 +1,7 @@ +# Genkit + +The sources for this package are in the main [Genkit](https://github.com/firebase/genkit) repo. Please file issues and pull requests against that repo. + +Usage information and reference details can be found in [Genkit documentation](https://genkit.dev/docs/get-started). + +License: Apache 2.0 diff --git a/js/shared/package.json b/js/shared/package.json new file mode 100644 index 0000000000..c1aa35d647 --- /dev/null +++ b/js/shared/package.json @@ -0,0 +1,49 @@ +{ + "name": "@genkit-ai/shared", + "description": "Genkit AI framework shared libraries", + "keywords": [ + "genkit", + "ai", + "genai", + "generative-ai" + ], + "version": "1.15.1", + "type": "commonjs", + "scripts": { + "check": "tsc", + "compile": "tsup-node", + "build:clean": "rimraf ./lib ./src/__codegen", + "build": "npm-run-all -s build:clean copy-types genversion check compile", + "build:watch": "tsup-node --watch", + "test": "node --import tsx --test tests/*_test.ts", + "genversion": "genversion -esf src/__codegen/version.ts", + "copy-types": "tsx scripts/copy-types.ts" + }, + "repository": { + "type": "git", + "url": "https://github.com/firebase/genkit.git", + "directory": "js/core" + }, + "author": "genkit", + "license": "Apache-2.0", + "dependencies": { + "zod": "^3.23.8" + }, + "devDependencies": { + "genversion": "^3.2.0", + "npm-run-all": "^4.1.5", + "rimraf": "^6.0.1", + "tsup": "^8.3.5", + "tsx": "^4.19.2", + "typescript": "^4.9.0" + }, + "types": "lib/index.d.ts", + "exports": { + ".": { + "require": "./lib/index.js", + "import": "./lib/index.mjs", + "types": "./lib/index.d.ts", + "default": "./lib/index.js" + } + } +} diff --git a/js/shared/scripts/copy-types.ts b/js/shared/scripts/copy-types.ts new file mode 100644 index 0000000000..4cff023749 --- /dev/null +++ b/js/shared/scripts/copy-types.ts @@ -0,0 +1,51 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as fs from 'fs'; +import * as path from 'path'; +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const sourceDir = path.resolve( + __dirname, + '../../../genkit-tools/common/src/types' +); +const destDir = path.resolve(__dirname, '../src/__codegen'); + +const filesToCopy = [ + 'document.ts', + 'embedder.ts', + 'evaluator.ts', + 'model.ts', + 'reranker.ts', + 'retriever.ts', + 'status.ts', + 'trace.ts', +]; + +if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, { recursive: true }); +} + +filesToCopy.forEach((file) => { + const sourceFile = path.join(sourceDir, file); + const destFile = path.join(destDir, file); + fs.copyFileSync(sourceFile, destFile); + console.log(`Copied ${file} to ${destDir}`); +}); diff --git a/js/shared/src/index.ts b/js/shared/src/index.ts new file mode 100644 index 0000000000..7e42dcbe23 --- /dev/null +++ b/js/shared/src/index.ts @@ -0,0 +1,17 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './types.js'; diff --git a/js/shared/src/types.ts b/js/shared/src/types.ts new file mode 100644 index 0000000000..d03784d3d4 --- /dev/null +++ b/js/shared/src/types.ts @@ -0,0 +1,261 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { z } from 'zod'; +import { + CustomPartSchema, + DataPartSchema, + DocumentDataSchema, + DocumentPartSchema, + MediaPartSchema, + MediaSchema, + ReasoningPartSchema, + TextPartSchema, + ToolRequestPartSchema, + ToolRequestSchema, + ToolResponsePartSchema, + ToolResponseSchema, +} from './__codegen/document.js'; +import { + EmbedRequestSchema, + EmbedResponseSchema, + EmbeddingSchema, + type EmbeddingBatch, +} from './__codegen/embedder.js'; +import { + BaseDataPointSchema, + BaseEvalDataPointSchema, + EvalFnResponseSchema, + EvalRequestSchema, + EvalResponseSchema, + EvalStatusEnumSchema, + ScoreSchema, +} from './__codegen/evaluator.js'; +import { + CandidateErrorSchema, + CandidateSchema, + FinishReasonSchema, + GenerateActionOptionsSchema, + GenerateActionOutputConfig, + GenerateRequestSchema, + GenerateResponseChunkSchema, + GenerateResponseSchema, + GenerationCommonConfigDescriptions, + GenerationCommonConfigSchema, + GenerationUsageSchema, + MessageSchema, + ModelInfoSchema, + ModelRequestSchema, + ModelResponseChunkSchema, + ModelResponseSchema, + OperationSchema, + OutputConfigSchema, + PartSchema, + RoleSchema, + ToolDefinitionSchema, +} from './__codegen/model.js'; +import { + CommonRerankerOptionsSchema, + RankedDocumentDataSchema, + RankedDocumentMetadataSchema, + RerankerRequestSchema, + RerankerResponseSchema, +} from './__codegen/reranker.js'; +import { + CommonRetrieverOptionsSchema, + RetrieverRequestSchema, + RetrieverResponseSchema, +} from './__codegen/retriever.js'; +import { Status, StatusCodes, StatusSchema } from './__codegen/status.js'; +import { + InstrumentationLibrarySchema, + LinkSchema, + NestedSpanDataSchema, + PathMetadataSchema, + SpanContextSchema, + SpanDataSchema, + SpanMetadataSchema, + SpanStatusSchema, + TimeEventSchema, + TraceDataSchema, + TraceMetadataSchema, +} from './__codegen/trace.js'; + +/** + * Background operation. + */ +export interface Operation { + action?: string; + id: string; + done?: boolean; + output?: O; + error?: { message: string; [key: string]: unknown }; + metadata?: Record; +} + +/** @deprecated All responses now return a single candidate. Only the first candidate will be used if supplied. */ +export type CandidateData = z.infer; + +export type CustomPart = z.infer; +export type DataPart = z.infer; +export type DocumentData = z.infer; +export type DocumentPart = z.infer; +export type MediaPart = z.infer; +export type Media = z.infer; +export type ReasoningPart = z.infer; +export type TextPart = z.infer; +export type ToolRequestPart = z.infer; +export type ToolRequest = z.infer; +export type ToolResponsePart = z.infer; +export type ToolResponse = z.infer; +export type EmbedRequest = z.infer; +export type EmbedResponse = z.infer; +export type Embedding = z.infer; +export type BaseDataPoint = z.infer; +export type BaseEvalDataPoint = z.infer; +export type EvalFnResponse = z.infer; +export type EvalRequest = z.infer; +export type EvalResponse = z.infer; +export type EvalStatusEnum = z.infer; +export type Score = z.infer; +export type FinishReason = z.infer; +export type GenerateActionOptions = z.infer; +export type GenerateRequestData = z.infer; + +/** + * Generate request. + */ +export interface GenerateRequest< + CustomOptionsSchema extends z.ZodTypeAny = z.ZodTypeAny, +> extends z.infer { + config?: z.infer; +} + +export type GenerateResponseChunkData = z.infer< + typeof GenerateResponseChunkSchema +>; +export type GenerateResponseData = z.infer; +export type GenerationCommonConfig = z.infer< + typeof GenerationCommonConfigSchema +>; +export type GenerationUsage = z.infer; +export type MessageData = z.infer; +export type ModelInfo = z.infer; + +/** ModelRequest represents the parameters that are passed to a model when generating content. */ +export interface ModelRequest< + CustomOptionsSchema extends z.ZodTypeAny = z.ZodTypeAny, +> extends z.infer { + config?: z.infer; +} + +export type ModelResponseChunkData = z.infer; +export type ModelResponseData = z.infer; +export type OutputConfig = z.infer; +export type Part = z.infer; +export type Role = z.infer; +export type ToolDefinition = z.infer; +export type CommonRerankerOptions = z.infer; +export type RankedDocumentData = z.infer; +export type RankedDocumentMetadata = z.infer< + typeof RankedDocumentMetadataSchema +>; +export type RerankerRequest = z.infer; +export type RerankerResponse = z.infer; +export type CommonRetrieverOptions = z.infer< + typeof CommonRetrieverOptionsSchema +>; +export type RetrieverRequest = z.infer; +export type RetrieverResponse = z.infer; +export type PathMetadata = z.infer; +export type TraceMetadata = z.infer; +export type SpanMetadata = z.infer; +export type SpanData = z.infer; +export type TraceData = z.infer; +export type NestedSpanData = z.infer; +export type Link = z.infer; + +/** @deprecated All responses now return a single candidate. Only the first candidate will be used if supplied. */ +export type CandidateError = z.infer; + +export { + BaseDataPointSchema, + BaseEvalDataPointSchema, + CandidateErrorSchema, + CandidateSchema, + CommonRerankerOptionsSchema, + CommonRetrieverOptionsSchema, + CustomPartSchema, + DataPartSchema, + DocumentDataSchema, + DocumentPartSchema, + EmbedRequestSchema, + EmbedResponseSchema, + EmbeddingSchema, + EvalFnResponseSchema, + EvalRequestSchema, + EvalResponseSchema, + EvalStatusEnumSchema, + FinishReasonSchema, + GenerateActionOptionsSchema, + GenerateActionOutputConfig, + GenerateRequestSchema, + GenerateResponseChunkSchema, + GenerateResponseSchema, + GenerationCommonConfigDescriptions, + GenerationCommonConfigSchema, + GenerationUsageSchema, + InstrumentationLibrarySchema, + LinkSchema, + MediaPartSchema, + MediaSchema, + MessageSchema, + ModelInfoSchema, + ModelRequestSchema, + ModelResponseChunkSchema, + ModelResponseSchema, + NestedSpanDataSchema, + OperationSchema, + OutputConfigSchema, + PartSchema, + PathMetadataSchema, + RankedDocumentDataSchema, + RankedDocumentMetadataSchema, + ReasoningPartSchema, + RerankerRequestSchema, + RerankerResponseSchema, + RetrieverRequestSchema, + RetrieverResponseSchema, + RoleSchema, + ScoreSchema, + SpanContextSchema, + SpanDataSchema, + SpanMetadataSchema, + SpanStatusSchema, + Status, + StatusCodes, + StatusSchema, + TextPartSchema, + TimeEventSchema, + ToolDefinitionSchema, + ToolRequestPartSchema, + ToolRequestSchema, + ToolResponsePartSchema, + ToolResponseSchema, + TraceDataSchema, + TraceMetadataSchema, + type EmbeddingBatch, +}; diff --git a/js/shared/tsconfig.json b/js/shared/tsconfig.json new file mode 100644 index 0000000000..d96b77bbbb --- /dev/null +++ b/js/shared/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "include": ["src"], + "compilerOptions": { + // DOM is needed for streaming APIs. + "lib": ["es2022", "dom"] + } +} diff --git a/js/shared/tsup.config.ts b/js/shared/tsup.config.ts new file mode 100644 index 0000000000..df5e371f8e --- /dev/null +++ b/js/shared/tsup.config.ts @@ -0,0 +1,22 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { defineConfig, type Options } from 'tsup'; +import { defaultOptions } from '../tsup.common'; + +export default defineConfig({ + ...(defaultOptions as Options), +}); diff --git a/js/shared/typedoc.json b/js/shared/typedoc.json new file mode 100644 index 0000000000..ea1ad0b8fd --- /dev/null +++ b/js/shared/typedoc.json @@ -0,0 +1,12 @@ +{ + "entryPoints": [ + "src/index.ts", + "src/metrics.ts", + "src/registry.ts", + "src/tracing.ts", + "src/logging.ts", + "src/config.ts", + "src/runtime.ts", + "src/schema.ts" + ] +} diff --git a/py/packages/genkit/src/genkit/core/typing.py b/py/packages/genkit/src/genkit/core/typing.py index 4f7a8877df..319d569566 100644 --- a/py/packages/genkit/src/genkit/core/typing.py +++ b/py/packages/genkit/src/genkit/core/typing.py @@ -56,6 +56,7 @@ class CustomPart(BaseModel): metadata: dict[str, Any] | None = None custom: dict[str, Any] reasoning: Any | None = None + resource: Any | None = None class Media(BaseModel): @@ -66,6 +67,13 @@ class Media(BaseModel): url: str +class Resource1(BaseModel): + """Model for resource1 data.""" + + model_config = ConfigDict(extra='forbid', populate_by_name=True) + uri: str + + class ToolRequest(BaseModel): """Model for toolrequest data.""" @@ -216,12 +224,30 @@ class GenerationCommonConfig(BaseModel): """Model for generationcommonconfig data.""" model_config = ConfigDict(extra='forbid', populate_by_name=True) - version: str | None = None - temperature: float | None = None - max_output_tokens: float | None = Field(None, alias='maxOutputTokens') - top_k: float | None = Field(None, alias='topK') - top_p: float | None = Field(None, alias='topP') - stop_sequences: list[str] | None = Field(None, alias='stopSequences') + version: str | None = Field( + None, description='A specific version of a model family, e.g. `gemini-2.0-flash` for the `googleai` family.' + ) + temperature: float | None = Field( + None, + description='Controls the degree of randomness in token selection. A lower value is good for a more predictable response. A higher value leads to more diverse or unexpected results.', + ) + max_output_tokens: float | None = Field( + None, alias='maxOutputTokens', description='The maximum number of tokens to include in the response.' + ) + top_k: float | None = Field( + None, alias='topK', description='The maximum number of tokens to consider when sampling.' + ) + top_p: float | None = Field( + None, + alias='topP', + description='Decides how many possible words to consider. A higher value means that the model looks at more possible words, even the less likely ones, which makes the generated text more diverse.', + ) + stop_sequences: list[str] | None = Field( + None, + alias='stopSequences', + description='Set of character sequences (up to 5) that will stop output generation.', + max_length=5, + ) class GenerationUsage(BaseModel): @@ -505,6 +531,12 @@ class Reasoning(RootModel[Any]): root: Any +class Resource(RootModel[Any]): + """Root model for resource.""" + + root: Any + + class Text(RootModel[Any]): """Root model for text.""" @@ -601,6 +633,7 @@ class DataPart(BaseModel): metadata: Metadata | None = None custom: dict[str, Any] | None = None reasoning: Reasoning | None = None + resource: Resource | None = None class MediaPart(BaseModel): @@ -615,6 +648,7 @@ class MediaPart(BaseModel): metadata: Metadata | None = None custom: Custom | None = None reasoning: Reasoning | None = None + resource: Resource | None = None class ReasoningPart(BaseModel): @@ -629,6 +663,22 @@ class ReasoningPart(BaseModel): metadata: Metadata | None = None custom: Custom | None = None reasoning: str + resource: Resource | None = None + + +class ResourcePart(BaseModel): + """Model for resourcepart data.""" + + model_config = ConfigDict(extra='forbid', populate_by_name=True) + text: Text | None = None + media: MediaModel | None = None + tool_request: ToolRequestModel | None = Field(None, alias='toolRequest') + tool_response: ToolResponseModel | None = Field(None, alias='toolResponse') + data: Data | None = None + metadata: Metadata | None = None + custom: Custom | None = None + reasoning: Reasoning | None = None + resource: Resource1 class TextPart(BaseModel): @@ -643,6 +693,7 @@ class TextPart(BaseModel): metadata: Metadata | None = None custom: Custom | None = None reasoning: Reasoning | None = None + resource: Resource | None = None class ToolRequestPart(BaseModel): @@ -657,6 +708,7 @@ class ToolRequestPart(BaseModel): metadata: Metadata | None = None custom: Custom | None = None reasoning: Reasoning | None = None + resource: Resource | None = None class ToolResponsePart(BaseModel): @@ -671,6 +723,7 @@ class ToolResponsePart(BaseModel): metadata: Metadata | None = None custom: Custom | None = None reasoning: Reasoning | None = None + resource: Resource | None = None class EmbedResponse(BaseModel): @@ -719,11 +772,15 @@ class Resume(BaseModel): class Part( - RootModel[TextPart | MediaPart | ToolRequestPart | ToolResponsePart | DataPart | CustomPart | ReasoningPart] + RootModel[ + TextPart | MediaPart | ToolRequestPart | ToolResponsePart | DataPart | CustomPart | ReasoningPart | ResourcePart + ] ): """Root model for part.""" - root: TextPart | MediaPart | ToolRequestPart | ToolResponsePart | DataPart | CustomPart | ReasoningPart + root: ( + TextPart | MediaPart | ToolRequestPart | ToolResponsePart | DataPart | CustomPart | ReasoningPart | ResourcePart + ) class Link(BaseModel): diff --git a/py/plugins/compat-oai/src/genkit/plugins/compat_oai/models/model_info.py b/py/plugins/compat-oai/src/genkit/plugins/compat_oai/models/model_info.py index 80253b9ab3..374deaf41a 100644 --- a/py/plugins/compat-oai/src/genkit/plugins/compat_oai/models/model_info.py +++ b/py/plugins/compat-oai/src/genkit/plugins/compat_oai/models/model_info.py @@ -183,9 +183,7 @@ def get_default_model_info(name: str) -> ModelInfo: supports=DEFAULT_SUPPORTS, ) + def get_default_openai_model_info(name: str) -> ModelInfo: """Gets the default model info given a name.""" - return ModelInfo( - label=f"OpenAI - {name}", - supports={'multiturn': True} - ) + return ModelInfo(label=f'OpenAI - {name}', supports={'multiturn': True}) diff --git a/py/plugins/compat-oai/src/genkit/plugins/compat_oai/openai_plugin.py b/py/plugins/compat-oai/src/genkit/plugins/compat_oai/openai_plugin.py index 6016ddef43..f7d9739254 100644 --- a/py/plugins/compat-oai/src/genkit/plugins/compat_oai/openai_plugin.py +++ b/py/plugins/compat-oai/src/genkit/plugins/compat_oai/openai_plugin.py @@ -16,6 +16,7 @@ """OpenAI OpenAI API Compatible Plugin for Genkit.""" + from functools import cached_property from typing import Any, Callable @@ -50,13 +51,11 @@ def open_ai_name(name: str) -> str: """ return f'openai/{name}' + def default_openai_metadata(name: str) -> dict[str, Any]: return { - 'model': { - 'label': f"OpenAI - {name}", - 'supports': {'multiturn': True} - }, - } + 'model': {'label': f'OpenAI - {name}', 'supports': {'multiturn': True}}, + } class OpenAI(Plugin): @@ -131,7 +130,6 @@ def resolve_action( # noqa: B027 kind: ActionKind, name: str, ) -> None: - if kind is not ActionKind.MODEL: return None @@ -175,17 +173,17 @@ def _define_openai_model(self, ai: GenkitRegistry, name: str) -> None: def list_actions(self) -> list[ActionMetadata]: """Generate a list of available actions or models. - Returns: - list[ActionMetadata]: A list of ActionMetadata objects, each with the following attributes: - - name (str): The name of the action or model. - - kind (ActionKind): The type or category of the action. - - info (dict): The metadata dictionary describing the model configuration and properties. - - config_schema (type): The schema class used for validating the model's configuration. + Returns: + list[ActionMetadata]: A list of ActionMetadata objects, each with the following attributes: + - name (str): The name of the action or model. + - kind (ActionKind): The type or category of the action. + - info (dict): The metadata dictionary describing the model configuration and properties. + - config_schema (type): The schema class used for validating the model's configuration. """ actions = [] models_ = self._openai_client.models.list() - models: list[Model] = models_.data + models: list[Model] = models_.data # Print each model for model in models: _name = model.id @@ -219,8 +217,6 @@ def list_actions(self) -> list[ActionMetadata]: return actions - - def openai_model(name: str) -> str: """Returns a string representing the OpenAI model name to use with Genkit. diff --git a/py/plugins/compat-oai/tests/test_plugin.py b/py/plugins/compat-oai/tests/test_plugin.py index 68f07bc784..c6ba1cf790 100644 --- a/py/plugins/compat-oai/tests/test_plugin.py +++ b/py/plugins/compat-oai/tests/test_plugin.py @@ -44,9 +44,7 @@ def test_openai_plugin_initialize() -> None: @pytest.mark.parametrize( 'kind, name', - [ - (ActionKind.MODEL, 'gpt-3.5-turbo') - ], + [(ActionKind.MODEL, 'gpt-3.5-turbo')], ) def test_openai_plugin_resolve_action(kind, name): """Unit Tests for resolve action method.""" @@ -72,7 +70,7 @@ def test_openai_plugin_resolve_action(kind, name): ], 'system_role': True, 'tools': True, - } + }, }, }, ) @@ -85,7 +83,7 @@ def test_openai_plugin_list_actions() -> None: Model(id='gpt-3.5-turbo', created=1677610602, object='model', owned_by='openai'), Model(id='o4-mini-deep-research-2025-06-26', created=1750866121, object='model', owned_by='system'), Model(id='codex-mini-latest', created=1746673257, object='model', owned_by='system'), - Model(id='text-embedding-ada-002', created=1671217299, object='model', owned_by='openai-internal') + Model(id='text-embedding-ada-002', created=1671217299, object='model', owned_by='openai-internal'), ] plugin = OpenAI(api_key='test-key') mock_client = MagicMock() @@ -96,7 +94,7 @@ def test_openai_plugin_list_actions() -> None: plugin._openai_client = mock_client - actions: list[ActionMetadata ] = plugin.list_actions + actions: list[ActionMetadata] = plugin.list_actions mock_client.models.list.assert_called_once() _ = plugin.list_actions mock_client.models.list.assert_called_once() @@ -108,9 +106,7 @@ def test_openai_plugin_list_actions() -> None: @pytest.mark.parametrize( 'kind, name', - [ - (ActionKind.MODEL, "model_doesnt_exist") - ], + [(ActionKind.MODEL, 'model_doesnt_exist')], ) def test_openai_plugin_resolve_action_not_found(kind, name): """Unit Tests for resolve action method.""" diff --git a/py/pyproject.toml b/py/pyproject.toml index 96d36ecf0d..e98b1cb26c 100644 --- a/py/pyproject.toml +++ b/py/pyproject.toml @@ -151,6 +151,9 @@ select = [ "F403", # wildcard imports "F841", # unused variables ] +ignore = [ + "E501", # line too long +] [tool.ruff.lint.isort] combine-as-imports = true