@opencode/ai 0.0.0-dev-20048 → 0.0.0-dev-20050
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/README.md +65 -1
- package/dist/generation.d.ts +13 -9
- package/dist/generation.js +11 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/promise.d.ts +164 -3
- package/dist/promise.js +11 -1
- package/dist/protocols/assemblyai-transcription.d.ts +40 -0
- package/dist/protocols/assemblyai-transcription.js +138 -0
- package/dist/protocols/deepgram-speech.js +17 -15
- package/dist/protocols/deepgram-transcription.d.ts +25 -0
- package/dist/protocols/deepgram-transcription.js +129 -0
- package/dist/protocols/gemini.d.ts +2 -2
- package/dist/protocols/gemini.js +2 -6
- package/dist/protocols/google-images.js +2 -14
- package/dist/protocols/google-speech.d.ts +2 -32
- package/dist/protocols/google-speech.js +22 -60
- package/dist/protocols/google-transcription.d.ts +173 -0
- package/dist/protocols/google-transcription.js +138 -0
- package/dist/protocols/openai-images.js +4 -7
- package/dist/protocols/openai-transcription.d.ts +207 -0
- package/dist/protocols/openai-transcription.js +190 -0
- package/dist/protocols/utils/gemini-generate-content.d.ts +65 -0
- package/dist/protocols/utils/gemini-generate-content.js +65 -0
- package/dist/protocols/utils/media-input.d.ts +4 -0
- package/dist/protocols/utils/media-input.js +14 -0
- package/dist/providers/assemblyai.d.ts +25 -0
- package/dist/providers/assemblyai.js +29 -0
- package/dist/providers/deepgram.d.ts +5 -0
- package/dist/providers/deepgram.js +5 -2
- package/dist/providers/google.d.ts +5 -0
- package/dist/providers/google.js +3 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/index.js +1 -0
- package/dist/providers/openai.d.ts +5 -0
- package/dist/providers/openai.js +4 -0
- package/dist/route/media-protocol.d.ts +15 -3
- package/dist/route/media-protocol.js +6 -0
- package/dist/route/media.d.ts +5 -2
- package/dist/route/media.js +62 -40
- package/dist/transcription-client.d.ts +28 -0
- package/dist/transcription-client.js +69 -0
- package/dist/transcription.d.ts +1504 -0
- package/dist/transcription.js +140 -0
- package/dist/utils/media-type.d.ts +1 -0
- package/dist/utils/media-type.js +14 -0
- package/dist/video-client.d.ts +1 -1
- package/dist/video-client.js +3 -6
- package/dist/video.d.ts +1 -15
- package/dist/video.js +2 -14
- package/package.json +3 -3
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Effect, Schema, Stream } from "effect";
|
|
2
|
+
import { Generation, ProgressEvent, QueuedEvent } from "./generation.js";
|
|
3
|
+
import { Media } from "./media.js";
|
|
4
|
+
import { MediaModel, composeRoute, tryRequest } from "./media-model.js";
|
|
5
|
+
import { MediaRoute } from "./route/media.js";
|
|
6
|
+
import { AIError, HttpOptions, MediaUsage, ProviderMetadata } from "./schema/index.js";
|
|
7
|
+
import { TranscriptionClient, Service } from "./transcription-client.js";
|
|
8
|
+
export class TranscriptionModel extends MediaModel {
|
|
9
|
+
static make(input) {
|
|
10
|
+
return new TranscriptionModel(input);
|
|
11
|
+
}
|
|
12
|
+
static fromRoute(route, input) {
|
|
13
|
+
const composed = isStreamInput(route)
|
|
14
|
+
? composeRoute((composition) => MediaRoute.stream({ ...composition, collect: collectResponse }), route, input)
|
|
15
|
+
: isQueuedInput(route)
|
|
16
|
+
? composeRoute(MediaRoute.queued, route, input)
|
|
17
|
+
: composeRoute(MediaRoute.inline, route, input);
|
|
18
|
+
return new TranscriptionModel({
|
|
19
|
+
id: input.id,
|
|
20
|
+
provider: route.provider,
|
|
21
|
+
http: input.http,
|
|
22
|
+
route: composed,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const isStreamInput = (route) => route.protocol.kind === "stream";
|
|
27
|
+
const isQueuedInput = (route) => route.protocol.kind === "queued";
|
|
28
|
+
export const TranscriptionModelSchema = Schema.declare((value) => value instanceof TranscriptionModel, { expected: "Transcription.Model" });
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Request
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
export const TranscriptionTimestamps = Schema.Literals(["none", "segment", "word"]);
|
|
33
|
+
export class TranscriptionRequest extends Schema.Class("Transcription.Request")({
|
|
34
|
+
model: TranscriptionModelSchema,
|
|
35
|
+
audio: Media.AssetSchema,
|
|
36
|
+
language: Schema.optional(Schema.String),
|
|
37
|
+
prompt: Schema.optional(Schema.String),
|
|
38
|
+
/** Routes that cannot produce the requested granularity fail typed; routes may return more than asked. */
|
|
39
|
+
timestamps: Schema.optional(TranscriptionTimestamps),
|
|
40
|
+
diarize: Schema.optional(Schema.Boolean),
|
|
41
|
+
speakers: Schema.optional(Schema.Int),
|
|
42
|
+
providerOptions: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
|
|
43
|
+
http: Schema.optional(HttpOptions),
|
|
44
|
+
}) {
|
|
45
|
+
}
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Response and events
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
/** Speaker labels are provider-native (`A`, `0`, `spk:0`, or a known speaker name). */
|
|
50
|
+
export const TranscriptionSegment = Schema.Struct({
|
|
51
|
+
text: Schema.String,
|
|
52
|
+
startSeconds: Schema.Number,
|
|
53
|
+
endSeconds: Schema.Number,
|
|
54
|
+
speaker: Schema.optional(Schema.String),
|
|
55
|
+
}).annotate({ identifier: "Transcription.Segment" });
|
|
56
|
+
export const TranscriptionWord = Schema.Struct({
|
|
57
|
+
text: Schema.String,
|
|
58
|
+
startSeconds: Schema.Number,
|
|
59
|
+
endSeconds: Schema.Number,
|
|
60
|
+
speaker: Schema.optional(Schema.String),
|
|
61
|
+
confidence: Schema.optional(Schema.Number),
|
|
62
|
+
}).annotate({ identifier: "Transcription.Word" });
|
|
63
|
+
const transcriptFields = {
|
|
64
|
+
text: Schema.String,
|
|
65
|
+
segments: Schema.optional(Schema.Array(TranscriptionSegment)),
|
|
66
|
+
words: Schema.optional(Schema.Array(TranscriptionWord)),
|
|
67
|
+
/** Provider-native language as detected or echoed (`en`, `english`, `en_us`), lowercased but not normalized. */
|
|
68
|
+
language: Schema.optional(Schema.String),
|
|
69
|
+
durationSeconds: Schema.optional(Schema.Number),
|
|
70
|
+
usage: Schema.optional(MediaUsage),
|
|
71
|
+
notices: Schema.optional(Schema.Array(Media.Notice)),
|
|
72
|
+
providerMetadata: Schema.optional(ProviderMetadata),
|
|
73
|
+
};
|
|
74
|
+
export class TranscriptionResponse extends Schema.Class("Transcription.Response")(transcriptFields) {
|
|
75
|
+
}
|
|
76
|
+
export const TranscriptionTextDeltaEvent = Schema.Struct({
|
|
77
|
+
type: Schema.tag("text-delta"),
|
|
78
|
+
delta: Schema.String,
|
|
79
|
+
}).annotate({ identifier: "Transcription.Event.TextDelta" });
|
|
80
|
+
export const TranscriptionSegmentEvent = Schema.Struct({
|
|
81
|
+
type: Schema.tag("segment"),
|
|
82
|
+
segment: TranscriptionSegment,
|
|
83
|
+
}).annotate({ identifier: "Transcription.Event.Segment" });
|
|
84
|
+
export const TranscriptionFinishEvent = Schema.Struct({
|
|
85
|
+
type: Schema.tag("finish"),
|
|
86
|
+
...transcriptFields,
|
|
87
|
+
}).annotate({ identifier: "Transcription.Event.Finish" });
|
|
88
|
+
const transcriptionEventTagged = Schema.Union([
|
|
89
|
+
QueuedEvent,
|
|
90
|
+
ProgressEvent,
|
|
91
|
+
TranscriptionTextDeltaEvent,
|
|
92
|
+
TranscriptionSegmentEvent,
|
|
93
|
+
TranscriptionFinishEvent,
|
|
94
|
+
]).pipe(Schema.toTaggedUnion("type"));
|
|
95
|
+
export const TranscriptionEvent = Object.assign(transcriptionEventTagged, {
|
|
96
|
+
is: {
|
|
97
|
+
generationQueued: transcriptionEventTagged.guards["generation-queued"],
|
|
98
|
+
generationProgress: transcriptionEventTagged.guards["generation-progress"],
|
|
99
|
+
textDelta: transcriptionEventTagged.guards["text-delta"],
|
|
100
|
+
segment: transcriptionEventTagged.guards.segment,
|
|
101
|
+
finish: transcriptionEventTagged.guards.finish,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
export const responseEvents = (response) => [
|
|
105
|
+
TranscriptionFinishEvent.make({ ...response }),
|
|
106
|
+
];
|
|
107
|
+
const collectResponse = (events) => {
|
|
108
|
+
const finish = events.find(TranscriptionEvent.is.finish);
|
|
109
|
+
// Every transcription protocol's `finish` emits the terminal event or fails, so a completed stream always has one.
|
|
110
|
+
if (finish === undefined)
|
|
111
|
+
return Effect.die(new Error("The transcription stream completed without a finish event"));
|
|
112
|
+
const { type: _type, ...transcript } = finish;
|
|
113
|
+
return Effect.succeed(new TranscriptionResponse(transcript));
|
|
114
|
+
};
|
|
115
|
+
export function request(input) {
|
|
116
|
+
if (input instanceof TranscriptionRequest)
|
|
117
|
+
return input;
|
|
118
|
+
return new TranscriptionRequest({
|
|
119
|
+
...input,
|
|
120
|
+
http: input.http === undefined ? undefined : HttpOptions.make(input.http),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
const requestEffect = (input) => tryRequest(() => request(input));
|
|
124
|
+
export function generate(input, options) {
|
|
125
|
+
return requestEffect(input).pipe(Effect.flatMap((request) => TranscriptionClient.generate(request, options)));
|
|
126
|
+
}
|
|
127
|
+
export function stream(input, options) {
|
|
128
|
+
return Stream.unwrap(requestEffect(input).pipe(Effect.map((request) => TranscriptionClient.stream(request, options))));
|
|
129
|
+
}
|
|
130
|
+
export function start(input) {
|
|
131
|
+
return requestEffect(input).pipe(Effect.flatMap((request) => TranscriptionClient.start(request)));
|
|
132
|
+
}
|
|
133
|
+
export const resume = (model, token) => TranscriptionClient.resume(model, token);
|
|
134
|
+
export const Transcription = {
|
|
135
|
+
request,
|
|
136
|
+
generate,
|
|
137
|
+
stream,
|
|
138
|
+
start,
|
|
139
|
+
resume,
|
|
140
|
+
};
|
package/dist/utils/media-type.js
CHANGED
|
@@ -45,6 +45,7 @@ const EXTENSIONS = {
|
|
|
45
45
|
mp4: "video/mp4",
|
|
46
46
|
webm: "video/webm",
|
|
47
47
|
mp3: "audio/mpeg",
|
|
48
|
+
m4a: "audio/mp4",
|
|
48
49
|
wav: "audio/wav",
|
|
49
50
|
ogg: "audio/ogg",
|
|
50
51
|
flac: "audio/flac",
|
|
@@ -54,3 +55,16 @@ const EXTENSIONS = {
|
|
|
54
55
|
csv: "text/csv",
|
|
55
56
|
};
|
|
56
57
|
export const extensionMediaType = (path) => EXTENSIONS[path.slice(path.lastIndexOf(".") + 1).toLowerCase()];
|
|
58
|
+
const EXTENSION_ALIASES = {
|
|
59
|
+
"audio/mp3": "mp3",
|
|
60
|
+
"audio/m4a": "m4a",
|
|
61
|
+
"audio/x-m4a": "m4a",
|
|
62
|
+
"audio/webm": "webm",
|
|
63
|
+
"audio/wave": "wav",
|
|
64
|
+
"audio/x-wav": "wav",
|
|
65
|
+
"audio/x-flac": "flac",
|
|
66
|
+
};
|
|
67
|
+
export const mediaTypeExtension = (mediaType) => {
|
|
68
|
+
const type = mediaType.split(";", 1)[0].trim().toLowerCase();
|
|
69
|
+
return EXTENSION_ALIASES[type] ?? Object.entries(EXTENSIONS).find(([, known]) => known === type)?.[0];
|
|
70
|
+
};
|
package/dist/video-client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Context, Effect, Layer, Stream } from "effect";
|
|
2
|
-
import type
|
|
2
|
+
import { type AwaitOptions, type Generation } from "./generation.js";
|
|
3
3
|
import { RequestExecutor } from "./route/executor.js";
|
|
4
4
|
import type { AIError } from "./schema/index.js";
|
|
5
5
|
import { type VideoEvent, type VideoModel, type VideoOptions, type VideoRequestFor, type VideoResponse } from "./video.js";
|
package/dist/video-client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Context, Effect, Layer, Stream } from "effect";
|
|
2
|
+
import { resultEvents } from "./generation.js";
|
|
2
3
|
import { RequestExecutor } from "./route/executor.js";
|
|
3
|
-
import {
|
|
4
|
+
import { responseEvents, } from "./video.js";
|
|
4
5
|
export class Service extends Context.Service()("@opencode/VideoClient") {
|
|
5
6
|
}
|
|
6
7
|
export const start = (request) => Effect.gen(function* () {
|
|
@@ -19,10 +20,6 @@ export const stream = (request, options) => Stream.unwrap(Effect.gen(function* (
|
|
|
19
20
|
const client = yield* Service;
|
|
20
21
|
return client.stream(request, options);
|
|
21
22
|
}));
|
|
22
|
-
/** Status observations until the first terminal one, then the fetched result expanded into events. */
|
|
23
|
-
const events = (generation, options) => generation
|
|
24
|
-
.events(options)
|
|
25
|
-
.pipe(Stream.filter(isObservation), Stream.concat(Stream.fromIterableEffect(Effect.map(generation.result(), responseEvents))));
|
|
26
23
|
export const layer = Layer.effect(Service, Effect.gen(function* () {
|
|
27
24
|
const executor = yield* RequestExecutor.Service;
|
|
28
25
|
const start = (request) => request.model.route.start(request, executor.execute);
|
|
@@ -30,7 +27,7 @@ export const layer = Layer.effect(Service, Effect.gen(function* () {
|
|
|
30
27
|
start,
|
|
31
28
|
resume: (model, token) => model.route.resume(model, token, executor.execute),
|
|
32
29
|
generate: (request, options) => start(request).pipe(Effect.flatMap((generation) => generation.await(options))),
|
|
33
|
-
stream: (request, options) => Stream.unwrap(start(request).pipe(Effect.map((generation) =>
|
|
30
|
+
stream: (request, options) => Stream.unwrap(start(request).pipe(Effect.map((generation) => resultEvents(generation, responseEvents, options)))),
|
|
34
31
|
});
|
|
35
32
|
}));
|
|
36
33
|
export const VideoClient = {
|
package/dist/video.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect, Schema, Stream } from "effect";
|
|
2
|
-
import { Generation, type AwaitOptions
|
|
2
|
+
import { Generation, type AwaitOptions } from "./generation.js";
|
|
3
3
|
import { Media } from "./media.js";
|
|
4
4
|
import { MediaModel } from "./media-model.js";
|
|
5
5
|
import { MediaRoute } from "./route/media.js";
|
|
@@ -304,16 +304,6 @@ declare const VideoResponse_base: Schema.Class<VideoResponse, Schema.Struct<{
|
|
|
304
304
|
export declare class VideoResponse extends VideoResponse_base {
|
|
305
305
|
get video(): Media.Asset;
|
|
306
306
|
}
|
|
307
|
-
export declare const VideoQueuedEvent: Schema.Struct<{
|
|
308
|
-
readonly type: Schema.tag<"generation-queued">;
|
|
309
|
-
readonly id: Schema.String;
|
|
310
|
-
readonly position: Schema.optional<Schema.Number>;
|
|
311
|
-
}>;
|
|
312
|
-
export declare const VideoProgressEvent: Schema.Struct<{
|
|
313
|
-
readonly type: Schema.tag<"generation-progress">;
|
|
314
|
-
readonly id: Schema.String;
|
|
315
|
-
readonly progress: Schema.optional<Schema.Number>;
|
|
316
|
-
}>;
|
|
317
307
|
export declare const VideoOutputEvent: Schema.Struct<{
|
|
318
308
|
readonly type: Schema.tag<"video">;
|
|
319
309
|
readonly index: Schema.Number;
|
|
@@ -1349,10 +1339,6 @@ export declare const VideoEvent: Schema.Union<readonly [Schema.Struct<{
|
|
|
1349
1339
|
export type VideoEvent = Schema.Schema.Type<typeof videoEventTagged>;
|
|
1350
1340
|
/** A completed response expanded into the streaming event shape. */
|
|
1351
1341
|
export declare const responseEvents: (response: VideoResponse) => ReadonlyArray<VideoEvent>;
|
|
1352
|
-
/** A status observation as a video event; the terminal observation is replaced by the result events, so `none`. */
|
|
1353
|
-
export declare const isObservation: (event: GenerationEvent) => event is Extract<VideoEvent, {
|
|
1354
|
-
type: "generation-queued" | "generation-progress";
|
|
1355
|
-
}>;
|
|
1356
1342
|
export declare function request<const Model extends VideoModel>(input: VideoRequestInput<Model>): VideoRequestFor<VideoModelOptions<Model>>;
|
|
1357
1343
|
export declare function request(input: VideoRequest): VideoRequest;
|
|
1358
1344
|
export declare function start<const Model extends VideoModel>(input: VideoRequestInput<Model>): Effect.Effect<Generation<VideoResponse>, AIError, Service>;
|
package/dist/video.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Effect, Schema, Stream } from "effect";
|
|
2
|
-
import { Generation } from "./generation.js";
|
|
2
|
+
import { Generation, ProgressEvent, QueuedEvent } from "./generation.js";
|
|
3
3
|
import { Media } from "./media.js";
|
|
4
4
|
import { MediaModel, composeRoute, tryRequest } from "./media-model.js";
|
|
5
5
|
import { MediaRoute } from "./route/media.js";
|
|
@@ -61,16 +61,6 @@ export class VideoResponse extends Schema.Class("Video.Response")({
|
|
|
61
61
|
return this.videos[0];
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
export const VideoQueuedEvent = Schema.Struct({
|
|
65
|
-
type: Schema.tag("generation-queued"),
|
|
66
|
-
id: Schema.String,
|
|
67
|
-
position: Schema.optional(Schema.Number),
|
|
68
|
-
}).annotate({ identifier: "Video.Event.Queued" });
|
|
69
|
-
export const VideoProgressEvent = Schema.Struct({
|
|
70
|
-
type: Schema.tag("generation-progress"),
|
|
71
|
-
id: Schema.String,
|
|
72
|
-
progress: Schema.optional(Schema.Number),
|
|
73
|
-
}).annotate({ identifier: "Video.Event.Progress" });
|
|
74
64
|
export const VideoOutputEvent = Schema.Struct({
|
|
75
65
|
type: Schema.tag("video"),
|
|
76
66
|
index: Schema.Number,
|
|
@@ -82,7 +72,7 @@ export const VideoFinishEvent = Schema.Struct({
|
|
|
82
72
|
notices: Schema.optional(Schema.Array(Media.Notice)),
|
|
83
73
|
providerMetadata: Schema.optional(ProviderMetadata),
|
|
84
74
|
}).annotate({ identifier: "Video.Event.Finish" });
|
|
85
|
-
const videoEventTagged = Schema.Union([
|
|
75
|
+
const videoEventTagged = Schema.Union([QueuedEvent, ProgressEvent, VideoOutputEvent, VideoFinishEvent]).pipe(Schema.toTaggedUnion("type"));
|
|
86
76
|
export const VideoEvent = Object.assign(videoEventTagged, {
|
|
87
77
|
is: {
|
|
88
78
|
generationQueued: videoEventTagged.guards["generation-queued"],
|
|
@@ -100,8 +90,6 @@ export const responseEvents = (response) => [
|
|
|
100
90
|
providerMetadata: response.providerMetadata,
|
|
101
91
|
}),
|
|
102
92
|
];
|
|
103
|
-
/** A status observation as a video event; the terminal observation is replaced by the result events, so `none`. */
|
|
104
|
-
export const isObservation = (event) => event.type !== "generation-finished";
|
|
105
93
|
export function request(input) {
|
|
106
94
|
if (input instanceof VideoRequest)
|
|
107
95
|
return input;
|
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-20050",
|
|
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-20050",
|
|
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-20050",
|
|
48
48
|
"aws4fetch": "1.0.20",
|
|
49
49
|
"effect": "4.0.0-rc.112",
|
|
50
50
|
"google-auth-library": "10.5.0"
|