@opencode/ai 0.0.0-dev-20208 → 0.0.0-dev-20210
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/protocols/gemini.js +1 -10
- package/dist/protocols/google-speech.js +9 -2
- package/dist/protocols/google-transcription.js +4 -0
- package/dist/protocols/utils/gemini-generate-content.d.ts +6 -0
- package/dist/protocols/utils/gemini-generate-content.js +33 -0
- package/dist/protocols/utils/speech-stream.d.ts +1 -0
- package/dist/protocols/utils/speech-stream.js +1 -0
- package/package.json +3 -3
package/dist/protocols/gemini.js
CHANGED
|
@@ -435,16 +435,7 @@ const mapFinishReason = (finishReason, hasToolCalls) => {
|
|
|
435
435
|
return hasToolCalls ? "tool-calls" : "stop";
|
|
436
436
|
if (finishReason === "MAX_TOKENS")
|
|
437
437
|
return "length";
|
|
438
|
-
if (finishReason
|
|
439
|
-
finishReason === "RECITATION" ||
|
|
440
|
-
finishReason === "SAFETY" ||
|
|
441
|
-
finishReason === "BLOCKLIST" ||
|
|
442
|
-
finishReason === "PROHIBITED_CONTENT" ||
|
|
443
|
-
finishReason === "SPII" ||
|
|
444
|
-
finishReason === "MODEL_ARMOR" ||
|
|
445
|
-
finishReason === "IMAGE_PROHIBITED_CONTENT" ||
|
|
446
|
-
finishReason === "IMAGE_RECITATION" ||
|
|
447
|
-
finishReason === "LANGUAGE")
|
|
438
|
+
if (GeminiGenerateContent.contentFiltered(finishReason))
|
|
448
439
|
return "content-filter";
|
|
449
440
|
if (finishReason === "MALFORMED_FUNCTION_CALL" ||
|
|
450
441
|
finishReason === "UNEXPECTED_TOOL_CALL" ||
|
|
@@ -49,9 +49,15 @@ const step = Effect.fn("GoogleSpeech.step")(function* (state, frame) {
|
|
|
49
49
|
return yield* blocked;
|
|
50
50
|
const audio = (chunk.candidates?.[0]?.content?.parts ?? []).flatMap((part) => part.inlineData === undefined ? [] : [part.inlineData]);
|
|
51
51
|
const next = { ...GeminiGenerateContent.track(state, chunk), mimeType: state.mimeType ?? audio[0]?.mimeType };
|
|
52
|
-
|
|
52
|
+
const events = audio.flatMap((part) => SpeechStream.delta(next, part.data)[1]);
|
|
53
|
+
const withheld = next.chunks.length === 0 ? GeminiGenerateContent.withheld(route.name, chunk, frame) : undefined;
|
|
54
|
+
if (withheld !== undefined)
|
|
55
|
+
return yield* withheld;
|
|
56
|
+
return [next, events];
|
|
53
57
|
});
|
|
54
58
|
const finish = (state, context) => {
|
|
59
|
+
if (state.finishReason === undefined)
|
|
60
|
+
return Effect.fail(route.incomplete());
|
|
55
61
|
const sampleRate = SpeechStream.sampleRate(state.mimeType) ?? DEFAULT_SAMPLE_RATE;
|
|
56
62
|
const output = state.mimeType?.split(";")[0]?.toLowerCase() === "audio/wav"
|
|
57
63
|
? SpeechStream.container("wav", sampleRate)
|
|
@@ -61,8 +67,9 @@ const finish = (state, context) => {
|
|
|
61
67
|
return SpeechStream.finish(route, state, {
|
|
62
68
|
...output,
|
|
63
69
|
usage: GeminiGenerateContent.usage(state.usage),
|
|
70
|
+
notices: GeminiGenerateContent.notices(route.name, state),
|
|
64
71
|
providerMetadata: GeminiGenerateContent.providerMetadata(state),
|
|
65
|
-
detail:
|
|
72
|
+
detail: `finish reason: ${state.finishReason}`,
|
|
66
73
|
});
|
|
67
74
|
};
|
|
68
75
|
// ---------------------------------------------------------------------------
|
|
@@ -85,6 +85,9 @@ const step = Effect.fn("GoogleTranscription.step")(function* (state, frame) {
|
|
|
85
85
|
.filter((item) => item.length > 0)
|
|
86
86
|
.join(" ");
|
|
87
87
|
const delta = text.length === 0 || state.text.length === 0 ? text : ` ${text}`;
|
|
88
|
+
const withheld = state.text.length + delta.length === 0 ? GeminiGenerateContent.withheld(route.name, chunk, frame) : undefined;
|
|
89
|
+
if (withheld !== undefined)
|
|
90
|
+
return yield* withheld;
|
|
88
91
|
const events = [
|
|
89
92
|
...(delta.length === 0 ? [] : [TranscriptionTextDeltaEvent.make({ delta })]),
|
|
90
93
|
...segments.map((segment) => TranscriptionSegmentEvent.make({ segment })),
|
|
@@ -100,6 +103,7 @@ const finish = (state) => {
|
|
|
100
103
|
segments: state.segments.length === 0 ? undefined : state.segments,
|
|
101
104
|
words: state.words.length === 0 ? undefined : state.words,
|
|
102
105
|
usage: GeminiGenerateContent.usage(state.usage),
|
|
106
|
+
notices: GeminiGenerateContent.notices(route.name, state),
|
|
103
107
|
providerMetadata: GeminiGenerateContent.providerMetadata(state),
|
|
104
108
|
}),
|
|
105
109
|
]);
|
|
@@ -47,6 +47,12 @@ export interface Metadata {
|
|
|
47
47
|
}
|
|
48
48
|
export declare const track: <State extends Metadata>(state: State, chunk: Chunk) => State;
|
|
49
49
|
export declare const blocked: (name: string, chunk: Chunk, frame: string) => AIError | undefined;
|
|
50
|
+
/** Finish reasons for which Gemini stops output on safety or policy grounds. */
|
|
51
|
+
export declare const contentFiltered: (finishReason: string | undefined) => boolean;
|
|
52
|
+
/** Callers check that the response produced no output: a policy stop after output is a partial result instead. */
|
|
53
|
+
export declare const withheld: (name: string, chunk: Chunk, frame: string) => AIError | undefined;
|
|
54
|
+
/** Any finish reason other than `STOP` means the output may be cut short, so it is surfaced rather than dropped. */
|
|
55
|
+
export declare const notices: (name: string, state: Metadata) => ReadonlyArray<Media.Notice> | undefined;
|
|
50
56
|
export declare const usage: (usage: UsageMetadata | undefined) => MediaUsage | undefined;
|
|
51
57
|
export declare const providerMetadata: (state: Metadata) => ProviderMetadata;
|
|
52
58
|
export declare const path: (model: string, mode: MediaProtocol.Mode) => string;
|
|
@@ -40,6 +40,39 @@ export const blocked = (name, chunk, frame) => {
|
|
|
40
40
|
}),
|
|
41
41
|
});
|
|
42
42
|
};
|
|
43
|
+
const CONTENT_FILTER_REASONS = new Set([
|
|
44
|
+
"IMAGE_SAFETY",
|
|
45
|
+
"RECITATION",
|
|
46
|
+
"SAFETY",
|
|
47
|
+
"BLOCKLIST",
|
|
48
|
+
"PROHIBITED_CONTENT",
|
|
49
|
+
"SPII",
|
|
50
|
+
"MODEL_ARMOR",
|
|
51
|
+
"IMAGE_PROHIBITED_CONTENT",
|
|
52
|
+
"IMAGE_RECITATION",
|
|
53
|
+
"LANGUAGE",
|
|
54
|
+
]);
|
|
55
|
+
/** Finish reasons for which Gemini stops output on safety or policy grounds. */
|
|
56
|
+
export const contentFiltered = (finishReason) => finishReason !== undefined && CONTENT_FILTER_REASONS.has(finishReason);
|
|
57
|
+
/** Callers check that the response produced no output: a policy stop after output is a partial result instead. */
|
|
58
|
+
export const withheld = (name, chunk, frame) => {
|
|
59
|
+
const finishReason = chunk.candidates?.[0]?.finishReason;
|
|
60
|
+
if (!contentFiltered(finishReason))
|
|
61
|
+
return undefined;
|
|
62
|
+
return new AIError({
|
|
63
|
+
reason: new ContentPolicyError({ message: `${name} withheld its output (${finishReason})`, body: frame }),
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
/** Any finish reason other than `STOP` means the output may be cut short, so it is surfaced rather than dropped. */
|
|
67
|
+
export const notices = (name, state) => state.finishReason === undefined || state.finishReason === "STOP"
|
|
68
|
+
? undefined
|
|
69
|
+
: [
|
|
70
|
+
{
|
|
71
|
+
type: contentFiltered(state.finishReason) ? "filtered" : "other",
|
|
72
|
+
message: `${name} finished with ${state.finishReason}`,
|
|
73
|
+
providerMetadata: { google: { finishReason: state.finishReason } },
|
|
74
|
+
},
|
|
75
|
+
];
|
|
43
76
|
export const usage = (usage) => usage === undefined
|
|
44
77
|
? undefined
|
|
45
78
|
: {
|
|
@@ -42,6 +42,7 @@ export declare const finish: (route: MediaProtocol.Identity, state: Audio, outpu
|
|
|
42
42
|
readonly mediaType: string | undefined;
|
|
43
43
|
readonly info?: Media.Info;
|
|
44
44
|
readonly usage?: MediaUsage;
|
|
45
|
+
readonly notices?: ReadonlyArray<Media.Notice>;
|
|
45
46
|
readonly providerMetadata?: ProviderMetadata;
|
|
46
47
|
readonly detail?: string;
|
|
47
48
|
}) => Effect.Effect<ReadonlyArray<SpeechEvent>, AIError>;
|
|
@@ -51,6 +51,7 @@ export const finish = (route, state, output) => {
|
|
|
51
51
|
SpeechFinishEvent.make({
|
|
52
52
|
audio: Media.bytes(concatBytes(state.chunks), output.mediaType, { info: output.info }),
|
|
53
53
|
usage: output.usage,
|
|
54
|
+
notices: output.notices,
|
|
54
55
|
providerMetadata: output.providerMetadata,
|
|
55
56
|
}),
|
|
56
57
|
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-20210",
|
|
4
4
|
"name": "@opencode/ai",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@clack/prompts": "1.0.0-alpha.1",
|
|
36
36
|
"@effect/platform-node": "4.0.0-rc.112",
|
|
37
|
-
"@opencode/http-recorder": "0.0.0-dev-
|
|
37
|
+
"@opencode/http-recorder": "0.0.0-dev-20210",
|
|
38
38
|
"@tsconfig/bun": "1.0.9",
|
|
39
39
|
"@types/bun": "1.4.0",
|
|
40
40
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"@aws-sdk/credential-providers": "3.1057.0",
|
|
45
45
|
"@smithy/eventstream-codec": "4.2.14",
|
|
46
46
|
"@smithy/util-utf8": "4.2.2",
|
|
47
|
-
"@opencode/schema": "0.0.0-dev-
|
|
47
|
+
"@opencode/schema": "0.0.0-dev-20210",
|
|
48
48
|
"aws4fetch": "1.0.20",
|
|
49
49
|
"effect": "4.0.0-rc.112",
|
|
50
50
|
"google-auth-library": "10.5.0"
|