@effect-uai/google 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Gemini.d.mts +3 -2
- package/dist/Gemini.d.mts.map +1 -1
- package/dist/Gemini.mjs +18 -6
- package/dist/Gemini.mjs.map +1 -1
- package/dist/GeminiEmbedding.mjs +1 -1
- package/dist/GeminiEmbedding.mjs.map +1 -1
- package/dist/GeminiSynthesizer.d.mts +4 -14
- package/dist/GeminiSynthesizer.d.mts.map +1 -1
- package/dist/GeminiSynthesizer.mjs +2 -2
- package/dist/GeminiSynthesizer.mjs.map +1 -1
- package/dist/GeminiSynthesizer.test.mjs +1 -1
- package/dist/GeminiTools.d.mts +42 -0
- package/dist/GeminiTools.d.mts.map +1 -0
- package/dist/GeminiTools.mjs +64 -0
- package/dist/GeminiTools.mjs.map +1 -0
- package/dist/GoogleDeepResearch.d.mts +70 -0
- package/dist/GoogleDeepResearch.d.mts.map +1 -0
- package/dist/GoogleDeepResearch.mjs +269 -0
- package/dist/GoogleDeepResearch.mjs.map +1 -0
- package/dist/LyriaGenerator.mjs +1 -1
- package/dist/LyriaGenerator.mjs.map +1 -1
- package/dist/LyriaGenerator.test.mjs +1 -1
- package/dist/codec.d.mts +16 -4
- package/dist/codec.d.mts.map +1 -1
- package/dist/codec.mjs +5 -6
- package/dist/codec.mjs.map +1 -1
- package/dist/{dist-DMsg8YLb.mjs → dist-CWoAuwS9.mjs} +7 -7
- package/dist/{dist-DMsg8YLb.mjs.map → dist-CWoAuwS9.mjs.map} +1 -1
- package/dist/geminiSpeechCodec.test.mjs +1 -1
- package/dist/geminiTools.test.d.mts +1 -0
- package/dist/geminiTools.test.mjs +80 -0
- package/dist/geminiTools.test.mjs.map +1 -0
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +3 -1
- package/dist/models.d.mts +11 -1
- package/dist/models.d.mts.map +1 -1
- package/package.json +8 -4
- package/src/Gemini.ts +21 -3
- package/src/GeminiEmbedding.ts +1 -1
- package/src/GeminiSynthesizer.ts +4 -2
- package/src/GeminiTools.ts +95 -0
- package/src/GoogleDeepResearch.ts +446 -0
- package/src/LyriaGenerator.ts +1 -1
- package/src/codec.ts +25 -11
- package/src/geminiTools.test.ts +88 -0
- package/src/index.ts +2 -0
- package/src/models.ts +15 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as it, n as globalExpect, r as describe } from "./dist-
|
|
1
|
+
import { i as it, n as globalExpect, r as describe } from "./dist-CWoAuwS9.mjs";
|
|
2
2
|
import { audioSourceToInlineData, wrapPcmAsWav } from "./geminiSpeechCodec.mjs";
|
|
3
3
|
import { Effect, Encoding } from "effect";
|
|
4
4
|
//#region src/geminiSpeechCodec.test.ts
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { codeExecutionTool, googleSearchTool, renderProviderTools } from "./GeminiTools.mjs";
|
|
2
|
+
import { WireChunk, buildRequestBody } from "./codec.mjs";
|
|
3
|
+
import { i as it, n as globalExpect, r as describe } from "./dist-CWoAuwS9.mjs";
|
|
4
|
+
import { Effect, Option, Result, Schema } from "effect";
|
|
5
|
+
import * as Tool from "@effect-uai/core/Tool";
|
|
6
|
+
import { descriptorsOf, providerToolsOf } from "@effect-uai/core/Tool";
|
|
7
|
+
//#region src/geminiTools.test.ts
|
|
8
|
+
const localTool = Tool.make({
|
|
9
|
+
name: "get_weather",
|
|
10
|
+
description: "Look up the weather.",
|
|
11
|
+
inputSchema: Tool.fromEffectSchema(Schema.Struct({ city: Schema.String })),
|
|
12
|
+
run: ({ city }) => Effect.succeed(`sunny in ${city}`)
|
|
13
|
+
});
|
|
14
|
+
const build = (toolkit) => {
|
|
15
|
+
const entries = Result.getOrThrow(renderProviderTools(providerToolsOf(toolkit)));
|
|
16
|
+
return buildRequestBody([], Option.none(), descriptorsOf(toolkit), entries);
|
|
17
|
+
};
|
|
18
|
+
describe("Gemini provider tools", () => {
|
|
19
|
+
it("renders google_search to the native googleSearch entry", () => {
|
|
20
|
+
globalExpect(build({ google_search: googleSearchTool }).tools).toEqual([{ googleSearch: {} }]);
|
|
21
|
+
});
|
|
22
|
+
it("renders a mixed toolkit to both a functionDeclarations entry and the native entry", () => {
|
|
23
|
+
globalExpect(build({
|
|
24
|
+
get_weather: localTool,
|
|
25
|
+
code_execution: codeExecutionTool
|
|
26
|
+
}).tools).toEqual([{ functionDeclarations: [globalExpect.objectContaining({
|
|
27
|
+
name: "get_weather",
|
|
28
|
+
description: "Look up the weather."
|
|
29
|
+
})] }, { codeExecution: {} }]);
|
|
30
|
+
});
|
|
31
|
+
it("omits toolConfig for a grounding-only request (no forced function-calling mode)", () => {
|
|
32
|
+
globalExpect(build({ google_search: googleSearchTool }).toolConfig).toBeUndefined();
|
|
33
|
+
});
|
|
34
|
+
it("sets toolConfig when a function declaration is present", () => {
|
|
35
|
+
globalExpect(build({
|
|
36
|
+
get_weather: localTool,
|
|
37
|
+
google_search: googleSearchTool
|
|
38
|
+
}).toolConfig).toEqual({ functionCallingConfig: { mode: "AUTO" } });
|
|
39
|
+
});
|
|
40
|
+
it("fails on a foreign provider tool", () => {
|
|
41
|
+
const result = renderProviderTools(providerToolsOf({ web_search: Tool.provider({
|
|
42
|
+
name: "web_search",
|
|
43
|
+
description: "Anthropic web search.",
|
|
44
|
+
inputSchema: Tool.noInput,
|
|
45
|
+
provider: "anthropic",
|
|
46
|
+
config: { type: "web_search_20250305" }
|
|
47
|
+
}) }));
|
|
48
|
+
globalExpect(Result.isFailure(result)).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
it("fails on an unrecognized config kind", () => {
|
|
51
|
+
const result = renderProviderTools(providerToolsOf({ mystery: Tool.provider({
|
|
52
|
+
name: "mystery",
|
|
53
|
+
description: "Unknown Gemini tool.",
|
|
54
|
+
inputSchema: Tool.noInput,
|
|
55
|
+
provider: "gemini",
|
|
56
|
+
config: { kind: "telepathy" }
|
|
57
|
+
}) }));
|
|
58
|
+
globalExpect(Result.isFailure(result)).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it("decodes a grounded chunk without throwing (groundingMetadata is dropped)", () => {
|
|
61
|
+
globalExpect(Schema.decodeUnknownSync(WireChunk)({ candidates: [{
|
|
62
|
+
content: {
|
|
63
|
+
role: "model",
|
|
64
|
+
parts: [{ text: "Grounded answer." }]
|
|
65
|
+
},
|
|
66
|
+
groundingMetadata: {
|
|
67
|
+
webSearchQueries: ["effect ts"],
|
|
68
|
+
groundingChunks: [{ web: {
|
|
69
|
+
uri: "https://example.com",
|
|
70
|
+
title: "Example"
|
|
71
|
+
} }]
|
|
72
|
+
},
|
|
73
|
+
finishReason: "STOP"
|
|
74
|
+
}] }).candidates?.[0]?.content?.parts?.[0]).toEqual({ text: "Grounded answer." });
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
//#endregion
|
|
78
|
+
export {};
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=geminiTools.test.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geminiTools.test.mjs","names":["expect"],"sources":["../src/geminiTools.test.ts"],"sourcesContent":["import { Effect, Option, Result, Schema } from \"effect\"\nimport * as Tool from \"@effect-uai/core/Tool\"\nimport { descriptorsOf, providerToolsOf } from \"@effect-uai/core/Tool\"\nimport { describe, expect, it } from \"vitest\"\nimport { WireChunk, buildRequestBody } from \"./codec.js\"\nimport { codeExecutionTool, googleSearchTool, renderProviderTools } from \"./GeminiTools.js\"\n\nconst localTool = Tool.make({\n name: \"get_weather\",\n description: \"Look up the weather.\",\n inputSchema: Tool.fromEffectSchema(Schema.Struct({ city: Schema.String })),\n run: ({ city }) => Effect.succeed(`sunny in ${city}`),\n})\n\nconst build = (toolkit: Record<string, Tool.AnyTool>) => {\n const entries = Result.getOrThrow(renderProviderTools(providerToolsOf(toolkit)))\n return buildRequestBody([], Option.none(), descriptorsOf(toolkit), entries)\n}\n\ndescribe(\"Gemini provider tools\", () => {\n it(\"renders google_search to the native googleSearch entry\", () => {\n const body = build({ google_search: googleSearchTool })\n expect(body.tools).toEqual([{ googleSearch: {} }])\n })\n\n it(\"renders a mixed toolkit to both a functionDeclarations entry and the native entry\", () => {\n const body = build({ get_weather: localTool, code_execution: codeExecutionTool })\n expect(body.tools).toEqual([\n {\n functionDeclarations: [\n expect.objectContaining({ name: \"get_weather\", description: \"Look up the weather.\" }),\n ],\n },\n { codeExecution: {} },\n ])\n })\n\n it(\"omits toolConfig for a grounding-only request (no forced function-calling mode)\", () => {\n const body = build({ google_search: googleSearchTool })\n expect(body.toolConfig).toBeUndefined()\n })\n\n it(\"sets toolConfig when a function declaration is present\", () => {\n const body = build({ get_weather: localTool, google_search: googleSearchTool })\n expect(body.toolConfig).toEqual({ functionCallingConfig: { mode: \"AUTO\" } })\n })\n\n it(\"fails on a foreign provider tool\", () => {\n const foreign = Tool.provider({\n name: \"web_search\",\n description: \"Anthropic web search.\",\n inputSchema: Tool.noInput,\n provider: \"anthropic\",\n config: { type: \"web_search_20250305\" },\n })\n const result = renderProviderTools(providerToolsOf({ web_search: foreign }))\n expect(Result.isFailure(result)).toBe(true)\n })\n\n it(\"fails on an unrecognized config kind\", () => {\n const bogus = Tool.provider({\n name: \"mystery\",\n description: \"Unknown Gemini tool.\",\n inputSchema: Tool.noInput,\n provider: \"gemini\",\n config: { kind: \"telepathy\" },\n })\n const result = renderProviderTools(providerToolsOf({ mystery: bogus }))\n expect(Result.isFailure(result)).toBe(true)\n })\n\n it(\"decodes a grounded chunk without throwing (groundingMetadata is dropped)\", () => {\n const grounded = {\n candidates: [\n {\n content: { role: \"model\", parts: [{ text: \"Grounded answer.\" }] },\n groundingMetadata: {\n webSearchQueries: [\"effect ts\"],\n groundingChunks: [{ web: { uri: \"https://example.com\", title: \"Example\" } }],\n },\n finishReason: \"STOP\",\n },\n ],\n }\n const chunk = Schema.decodeUnknownSync(WireChunk)(grounded)\n expect(chunk.candidates?.[0]?.content?.parts?.[0]).toEqual({ text: \"Grounded answer.\" })\n })\n})\n"],"mappings":";;;;;;;AAOA,MAAM,YAAY,KAAK,KAAK;CAC1B,MAAM;CACN,aAAa;CACb,aAAa,KAAK,iBAAiB,OAAO,OAAO,EAAE,MAAM,OAAO,QAAQ,CAAC,CAAC;CAC1E,MAAM,EAAE,WAAW,OAAO,QAAQ,YAAY,OAAO;CACtD,CAAC;AAEF,MAAM,SAAS,YAA0C;CACvD,MAAM,UAAU,OAAO,WAAW,oBAAoB,gBAAgB,QAAQ,CAAC,CAAC;AAChF,QAAO,iBAAiB,EAAE,EAAE,OAAO,MAAM,EAAE,cAAc,QAAQ,EAAE,QAAQ;;AAG7E,SAAS,+BAA+B;AACtC,IAAG,gEAAgE;AAEjE,eADa,MAAM,EAAE,eAAe,kBAAkB,CAC3C,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC;GAClD;AAEF,IAAG,2FAA2F;AAE5F,eADa,MAAM;GAAE,aAAa;GAAW,gBAAgB;GAAmB,CACrE,CAAC,MAAM,CAAC,QAAQ,CACzB,EACE,sBAAsB,CACpBA,aAAO,iBAAiB;GAAE,MAAM;GAAe,aAAa;GAAwB,CAAC,CACtF,EACF,EACD,EAAE,eAAe,EAAE,EAAE,CACtB,CAAC;GACF;AAEF,IAAG,yFAAyF;AAE1F,eADa,MAAM,EAAE,eAAe,kBAAkB,CAC3C,CAAC,WAAW,CAAC,eAAe;GACvC;AAEF,IAAG,gEAAgE;AAEjE,eADa,MAAM;GAAE,aAAa;GAAW,eAAe;GAAkB,CACnE,CAAC,WAAW,CAAC,QAAQ,EAAE,uBAAuB,EAAE,MAAM,QAAQ,EAAE,CAAC;GAC5E;AAEF,IAAG,0CAA0C;EAQ3C,MAAM,SAAS,oBAAoB,gBAAgB,EAAE,YAPrC,KAAK,SAAS;GAC5B,MAAM;GACN,aAAa;GACb,aAAa,KAAK;GAClB,UAAU;GACV,QAAQ,EAAE,MAAM,uBAAuB;GACxC,CACuE,EAAE,CAAC,CAAC;AAC5E,eAAO,OAAO,UAAU,OAAO,CAAC,CAAC,KAAK,KAAK;GAC3C;AAEF,IAAG,8CAA8C;EAQ/C,MAAM,SAAS,oBAAoB,gBAAgB,EAAE,SAPvC,KAAK,SAAS;GAC1B,MAAM;GACN,aAAa;GACb,aAAa,KAAK;GAClB,UAAU;GACV,QAAQ,EAAE,MAAM,aAAa;GAC9B,CACkE,EAAE,CAAC,CAAC;AACvE,eAAO,OAAO,UAAU,OAAO,CAAC,CAAC,KAAK,KAAK;GAC3C;AAEF,IAAG,kFAAkF;AAcnF,eADc,OAAO,kBAAkB,UAAU,CAAC,EAXhD,YAAY,CACV;GACE,SAAS;IAAE,MAAM;IAAS,OAAO,CAAC,EAAE,MAAM,oBAAoB,CAAC;IAAE;GACjE,mBAAmB;IACjB,kBAAkB,CAAC,YAAY;IAC/B,iBAAiB,CAAC,EAAE,KAAK;KAAE,KAAK;KAAuB,OAAO;KAAW,EAAE,CAAC;IAC7E;GACD,cAAc;GACf,CACF,EAEuD,CAC9C,CAAC,aAAa,IAAI,SAAS,QAAQ,GAAG,CAAC,QAAQ,EAAE,MAAM,oBAAoB,CAAC;GACxF;EACF"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { t as codec_d_exports } from "./codec.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { t as GeminiTools_d_exports } from "./GeminiTools.mjs";
|
|
3
|
+
import { GeminiTtsModel, GeminiVoiceName, GoogleDeepResearchModel, GoogleEmbeddingModel, GoogleModel, LyriaModel } from "./models.mjs";
|
|
3
4
|
import { t as Gemini_d_exports } from "./Gemini.mjs";
|
|
4
5
|
import { t as GeminiEmbedding_d_exports } from "./GeminiEmbedding.mjs";
|
|
5
6
|
import { t as GeminiSynthesizer_d_exports } from "./GeminiSynthesizer.mjs";
|
|
7
|
+
import { t as GoogleDeepResearch_d_exports } from "./GoogleDeepResearch.mjs";
|
|
6
8
|
import { t as LyriaGenerator_d_exports } from "./LyriaGenerator.mjs";
|
|
7
|
-
export { Gemini_d_exports as Gemini, GeminiEmbedding_d_exports as GeminiEmbedding, GeminiSynthesizer_d_exports as GeminiSynthesizer, GeminiTtsModel, GeminiVoiceName, GoogleEmbeddingModel, GoogleModel, LyriaGenerator_d_exports as LyriaGenerator, LyriaModel, codec_d_exports as codec };
|
|
9
|
+
export { Gemini_d_exports as Gemini, GeminiEmbedding_d_exports as GeminiEmbedding, GeminiSynthesizer_d_exports as GeminiSynthesizer, GeminiTools_d_exports as GeminiTools, GeminiTtsModel, GeminiVoiceName, GoogleDeepResearch_d_exports as GoogleDeepResearch, GoogleDeepResearchModel, GoogleEmbeddingModel, GoogleModel, LyriaGenerator_d_exports as LyriaGenerator, LyriaModel, codec_d_exports as codec };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { t as GeminiTools_exports } from "./GeminiTools.mjs";
|
|
1
2
|
import { t as codec_exports } from "./codec.mjs";
|
|
2
3
|
import { t as Gemini_exports } from "./Gemini.mjs";
|
|
3
4
|
import { t as GeminiEmbedding_exports } from "./GeminiEmbedding.mjs";
|
|
4
5
|
import { t as GeminiSynthesizer_exports } from "./GeminiSynthesizer.mjs";
|
|
6
|
+
import { t as GoogleDeepResearch_exports } from "./GoogleDeepResearch.mjs";
|
|
5
7
|
import { t as LyriaGenerator_exports } from "./LyriaGenerator.mjs";
|
|
6
8
|
import "./models.mjs";
|
|
7
|
-
export { Gemini_exports as Gemini, GeminiEmbedding_exports as GeminiEmbedding, GeminiSynthesizer_exports as GeminiSynthesizer, LyriaGenerator_exports as LyriaGenerator, codec_exports as codec };
|
|
9
|
+
export { Gemini_exports as Gemini, GeminiEmbedding_exports as GeminiEmbedding, GeminiSynthesizer_exports as GeminiSynthesizer, GeminiTools_exports as GeminiTools, GoogleDeepResearch_exports as GoogleDeepResearch, LyriaGenerator_exports as LyriaGenerator, codec_exports as codec };
|
package/dist/models.d.mts
CHANGED
|
@@ -7,6 +7,16 @@
|
|
|
7
7
|
* Reference: https://ai.google.dev/gemini-api/docs/models
|
|
8
8
|
*/
|
|
9
9
|
type GoogleModel = "gemini-3.1-pro-preview" | "gemini-3-flash-preview" | "gemini-3.1-flash-lite-preview" | "gemini-3.1-flash-live-preview" | "gemini-3.1-flash-tts-preview" | "gemini-2.5-pro" | "gemini-2.5-flash" | "gemini-2.5-flash-lite" | (string & {});
|
|
10
|
+
/**
|
|
11
|
+
* Deep-research agent identifiers for the Interactions API (`POST
|
|
12
|
+
* /v1beta/interactions`). `-preview` is the fast tier, `-max-` the
|
|
13
|
+
* comprehensive one. Preview surface: verify against live docs before relying on
|
|
14
|
+
* exact ids. The `(string & {})` tail accepts any string so a renamed agent
|
|
15
|
+
* works without an SDK update.
|
|
16
|
+
*
|
|
17
|
+
* Reference: https://ai.google.dev/gemini-api/docs/interactions/deep-research
|
|
18
|
+
*/
|
|
19
|
+
type GoogleDeepResearchModel = "deep-research-preview-04-2026" | "deep-research-max-preview-04-2026" | (string & {});
|
|
10
20
|
/**
|
|
11
21
|
* Known Gemini embedding model identifiers.
|
|
12
22
|
*
|
|
@@ -48,5 +58,5 @@ type GeminiVoiceName = "Zephyr" | "Puck" | "Charon" | "Kore" | "Fenrir" | "Leda"
|
|
|
48
58
|
type LyriaModel = "lyria-3-clip-preview" | "lyria-3-pro-preview" | (string & {});
|
|
49
59
|
type GoogleEmbeddingModel = "gemini-embedding-2" | "gemini-embedding-001" | (string & {});
|
|
50
60
|
//#endregion
|
|
51
|
-
export { GeminiTtsModel, GeminiVoiceName, GoogleEmbeddingModel, GoogleModel, LyriaModel };
|
|
61
|
+
export { GeminiTtsModel, GeminiVoiceName, GoogleDeepResearchModel, GoogleEmbeddingModel, GoogleModel, LyriaModel };
|
|
52
62
|
//# sourceMappingURL=models.d.mts.map
|
package/dist/models.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.mts","names":[],"sources":["../src/models.ts"],"mappings":";;AAOA;;;;;
|
|
1
|
+
{"version":3,"file":"models.d.mts","names":[],"sources":["../src/models.ts"],"mappings":";;AAOA;;;;;AAqBA;KArBY,WAAA;;;;AA+CZ;;;;;AAcA;KAxCY,uBAAA;;;;AAiFZ;;;;;AAMA;;;;;;;;;;;;KA7DY,cAAA;;;;;;;;KAcA,eAAA;;;;;;;;;;KAyCA,UAAA;AAAA,KAMA,oBAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-uai/google",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Google Gemini provider for @effect-uai/core.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -46,6 +46,10 @@
|
|
|
46
46
|
"types": "./dist/GeminiSynthesizer.d.mts",
|
|
47
47
|
"import": "./dist/GeminiSynthesizer.mjs"
|
|
48
48
|
},
|
|
49
|
+
"./GoogleDeepResearch": {
|
|
50
|
+
"types": "./dist/GoogleDeepResearch.d.mts",
|
|
51
|
+
"import": "./dist/GoogleDeepResearch.mjs"
|
|
52
|
+
},
|
|
49
53
|
"./LyriaGenerator": {
|
|
50
54
|
"types": "./dist/LyriaGenerator.d.mts",
|
|
51
55
|
"import": "./dist/LyriaGenerator.mjs"
|
|
@@ -55,13 +59,13 @@
|
|
|
55
59
|
"access": "public"
|
|
56
60
|
},
|
|
57
61
|
"devDependencies": {
|
|
58
|
-
"effect": "4.0.0-beta.
|
|
62
|
+
"effect": "4.0.0-beta.94",
|
|
59
63
|
"typescript": "^6.0.3",
|
|
60
|
-
"@effect-uai/core": "0.
|
|
64
|
+
"@effect-uai/core": "0.11.0"
|
|
61
65
|
},
|
|
62
66
|
"peerDependencies": {
|
|
63
67
|
"@effect-uai/core": ">=0.2.0 <1",
|
|
64
|
-
"effect": "4.0.0-beta.
|
|
68
|
+
"effect": ">=4.0.0-beta.94 <5.0.0"
|
|
65
69
|
},
|
|
66
70
|
"scripts": {
|
|
67
71
|
"build": "tsdown",
|
package/src/Gemini.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Context, Effect, Layer, Option, Redacted, Schema, Stream } from "effect"
|
|
1
|
+
import { Context, Effect, Layer, Option, Redacted, Result, Schema, Stream } from "effect"
|
|
2
2
|
import { HttpClient, HttpClientRequest } from "effect/unstable/http"
|
|
3
3
|
import * as AiError from "@effect-uai/core/AiError"
|
|
4
4
|
import {
|
|
@@ -9,8 +9,11 @@ import {
|
|
|
9
9
|
} from "@effect-uai/core/LanguageModel"
|
|
10
10
|
import { JsonParseError } from "@effect-uai/core/JSONL"
|
|
11
11
|
import * as SSE from "@effect-uai/core/SSE"
|
|
12
|
-
import { descriptorsOf } from "@effect-uai/core/Tool"
|
|
12
|
+
import { descriptorsOf, providerToolsOf } from "@effect-uai/core/Tool"
|
|
13
13
|
import { type Turn, TurnEvent } from "@effect-uai/core/Turn"
|
|
14
|
+
import { renderProviderTools } from "./GeminiTools.js"
|
|
15
|
+
|
|
16
|
+
export { codeExecutionTool, googleSearchTool, urlContextTool } from "./GeminiTools.js"
|
|
14
17
|
import {
|
|
15
18
|
type ChunkPart,
|
|
16
19
|
type GenerationConfig,
|
|
@@ -174,10 +177,25 @@ const buildNativeStream = (cfg: Config) => {
|
|
|
174
177
|
}
|
|
175
178
|
const url = `${baseUrl}/models/${request.model}:streamGenerateContent?alt=sse`
|
|
176
179
|
const generationConfig = buildGenerationConfig(request)
|
|
180
|
+
const providerToolEntries = yield* Result.match(
|
|
181
|
+
renderProviderTools(providerToolsOf(request.tools)),
|
|
182
|
+
{
|
|
183
|
+
onFailure: (e) =>
|
|
184
|
+
Effect.fail(
|
|
185
|
+
new AiError.Unsupported({
|
|
186
|
+
provider: "gemini",
|
|
187
|
+
capability: e.capability,
|
|
188
|
+
reason: e.reason,
|
|
189
|
+
}),
|
|
190
|
+
),
|
|
191
|
+
onSuccess: (entries) => Effect.succeed(entries),
|
|
192
|
+
},
|
|
193
|
+
)
|
|
177
194
|
const body = buildRequestBody(
|
|
178
195
|
request.history,
|
|
179
196
|
generationConfig,
|
|
180
197
|
descriptorsOf(request.tools),
|
|
198
|
+
providerToolEntries,
|
|
181
199
|
request.toolChoice,
|
|
182
200
|
)
|
|
183
201
|
const httpRequest = HttpClientRequest.post(url).pipe(
|
|
@@ -287,7 +305,7 @@ export const toCanonical = <E, R>(
|
|
|
287
305
|
* Build a `GeminiService` value. For Layer-based setup, prefer `layer`.
|
|
288
306
|
*/
|
|
289
307
|
export const make = (cfg: Config): Effect.Effect<GeminiService, never, HttpClient.HttpClient> =>
|
|
290
|
-
Effect.map(HttpClient.HttpClient
|
|
308
|
+
Effect.map(HttpClient.HttpClient, (client) => {
|
|
291
309
|
const streamNative: GeminiService["streamNative"] = (request) =>
|
|
292
310
|
buildNativeStream(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client))
|
|
293
311
|
const streamTurn: GeminiService["streamTurn"] = (request) => toCanonical(streamNative(request))
|
package/src/GeminiEmbedding.ts
CHANGED
|
@@ -328,7 +328,7 @@ const embedManyImpl =
|
|
|
328
328
|
export const make = (
|
|
329
329
|
cfg: Config,
|
|
330
330
|
): Effect.Effect<GeminiEmbeddingService, never, HttpClient.HttpClient> =>
|
|
331
|
-
Effect.map(HttpClient.HttpClient
|
|
331
|
+
Effect.map(HttpClient.HttpClient, (client) => ({
|
|
332
332
|
embed: (request) =>
|
|
333
333
|
embedImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),
|
|
334
334
|
embedMany: (request) =>
|
package/src/GeminiSynthesizer.ts
CHANGED
|
@@ -150,7 +150,9 @@ const synthesizeImpl = (cfg: Config) => (request: GeminiSynthesizeRequest) =>
|
|
|
150
150
|
reason: "Gemini `:generateContent` TTS has no language parameter.",
|
|
151
151
|
})
|
|
152
152
|
const client = yield* HttpClient.HttpClient
|
|
153
|
-
const [format, wrap] = yield*
|
|
153
|
+
const [format, wrap] = yield* Effect.fromResult(
|
|
154
|
+
realizeOutput(request.outputFormat?.container ?? "raw"),
|
|
155
|
+
)
|
|
154
156
|
const httpRequest = HttpClientRequest.post(
|
|
155
157
|
`${baseUrl(cfg)}/models/${request.model}:generateContent`,
|
|
156
158
|
).pipe(
|
|
@@ -221,7 +223,7 @@ const unsupportedStreamDialogue: SpeechSynthesizerService["streamSynthesizeDialo
|
|
|
221
223
|
|
|
222
224
|
export const make = (cfg: Config) =>
|
|
223
225
|
Effect.map(
|
|
224
|
-
HttpClient.HttpClient
|
|
226
|
+
HttpClient.HttpClient,
|
|
225
227
|
(client): GeminiSynthesizerService => ({
|
|
226
228
|
synthesize: (r) =>
|
|
227
229
|
synthesizeImpl(cfg)(r).pipe(Effect.provideService(HttpClient.HttpClient, client)),
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Match, Result, Schema } from "effect"
|
|
2
|
+
import * as Tool from "@effect-uai/core/Tool"
|
|
3
|
+
import type { RequestTool } from "./codec.js"
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Gemini provider-hosted tools. Each is a `Tool.provider` whose `config`
|
|
7
|
+
// carries the discriminant the renderer maps to a native `tools[]` entry
|
|
8
|
+
// (`googleSearch`, `urlContext`, `codeExecution`). The `inputSchema` is never
|
|
9
|
+
// sent — provider tools are partitioned out of the function declarations — so
|
|
10
|
+
// `Tool.noInput` suffices.
|
|
11
|
+
// Reference: https://ai.google.dev/api/caching#Tool
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
/** Discriminated config for a Gemini hosted tool. */
|
|
15
|
+
export const GeminiToolConfig = Schema.Union([
|
|
16
|
+
Schema.Struct({ kind: Schema.Literal("google_search") }),
|
|
17
|
+
Schema.Struct({ kind: Schema.Literal("url_context") }),
|
|
18
|
+
Schema.Struct({ kind: Schema.Literal("code_execution") }),
|
|
19
|
+
])
|
|
20
|
+
export type GeminiToolConfig = typeof GeminiToolConfig.Type
|
|
21
|
+
|
|
22
|
+
/** Google Search grounding, executed by Gemini. */
|
|
23
|
+
export const googleSearchTool = Tool.provider({
|
|
24
|
+
name: "google_search",
|
|
25
|
+
description: "Ground the answer with Google Search, executed by Gemini.",
|
|
26
|
+
inputSchema: Tool.noInput,
|
|
27
|
+
provider: "gemini",
|
|
28
|
+
config: { kind: "google_search" } as const,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
/** URL context: Gemini fetches and reads URLs referenced in the prompt. */
|
|
32
|
+
export const urlContextTool = Tool.provider({
|
|
33
|
+
name: "url_context",
|
|
34
|
+
description: "Let Gemini fetch and read URLs referenced in the prompt.",
|
|
35
|
+
inputSchema: Tool.noInput,
|
|
36
|
+
provider: "gemini",
|
|
37
|
+
config: { kind: "url_context" } as const,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
/** Code execution: Gemini runs generated Python in a sandbox. */
|
|
41
|
+
export const codeExecutionTool = Tool.provider({
|
|
42
|
+
name: "code_execution",
|
|
43
|
+
description: "Let Gemini write and run Python in a sandbox.",
|
|
44
|
+
inputSchema: Tool.noInput,
|
|
45
|
+
provider: "gemini",
|
|
46
|
+
config: { kind: "code_execution" } as const,
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A provider tool this adapter cannot render — a foreign `provider` or an
|
|
51
|
+
* unrecognized `config`. Carried as data so `Gemini.ts` can lift it into a
|
|
52
|
+
* typed `AiError.Unsupported`, mirroring how `hasUrlImageSource` surfaces a
|
|
53
|
+
* request-shaping failure.
|
|
54
|
+
*/
|
|
55
|
+
export type ToolRenderError = {
|
|
56
|
+
readonly capability: string
|
|
57
|
+
readonly reason: string
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const decodeConfig = Schema.decodeUnknownResult(GeminiToolConfig)
|
|
61
|
+
|
|
62
|
+
const configToWire = Match.type<GeminiToolConfig>().pipe(
|
|
63
|
+
Match.discriminatorsExhaustive("kind")({
|
|
64
|
+
google_search: (): RequestTool => ({ googleSearch: {} }),
|
|
65
|
+
url_context: (): RequestTool => ({ urlContext: {} }),
|
|
66
|
+
code_execution: (): RequestTool => ({ codeExecution: {} }),
|
|
67
|
+
}),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
const renderOne = (
|
|
71
|
+
tool: Tool.ProviderTool<string, any, string, any>,
|
|
72
|
+
): Result.Result<RequestTool, ToolRenderError> =>
|
|
73
|
+
tool.provider !== "gemini"
|
|
74
|
+
? Result.fail({
|
|
75
|
+
capability: `providerTool:${tool.provider}`,
|
|
76
|
+
reason: `tool "${tool.name}" is hosted by "${tool.provider}", not gemini`,
|
|
77
|
+
})
|
|
78
|
+
: decodeConfig(tool.config).pipe(
|
|
79
|
+
Result.map(configToWire),
|
|
80
|
+
Result.mapError(
|
|
81
|
+
(): ToolRenderError => ({
|
|
82
|
+
capability: "providerTool",
|
|
83
|
+
reason: `unrecognized Gemini provider tool config on "${tool.name}"`,
|
|
84
|
+
}),
|
|
85
|
+
),
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Render a toolkit's Gemini provider tools to native `tools[]` entries. Fails
|
|
90
|
+
* with a `ToolRenderError` on the first foreign or unrecognized tool rather
|
|
91
|
+
* than dropping it.
|
|
92
|
+
*/
|
|
93
|
+
export const renderProviderTools = (
|
|
94
|
+
tools: ReadonlyArray<Tool.ProviderTool<string, any, string, any>>,
|
|
95
|
+
): Result.Result<ReadonlyArray<RequestTool>, ToolRenderError> => Result.all(tools.map(renderOne))
|