@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.
Files changed (51) hide show
  1. package/README.md +65 -1
  2. package/dist/generation.d.ts +13 -9
  3. package/dist/generation.js +11 -0
  4. package/dist/index.d.ts +4 -0
  5. package/dist/index.js +3 -0
  6. package/dist/promise.d.ts +164 -3
  7. package/dist/promise.js +11 -1
  8. package/dist/protocols/assemblyai-transcription.d.ts +40 -0
  9. package/dist/protocols/assemblyai-transcription.js +138 -0
  10. package/dist/protocols/deepgram-speech.js +17 -15
  11. package/dist/protocols/deepgram-transcription.d.ts +25 -0
  12. package/dist/protocols/deepgram-transcription.js +129 -0
  13. package/dist/protocols/gemini.d.ts +2 -2
  14. package/dist/protocols/gemini.js +2 -6
  15. package/dist/protocols/google-images.js +2 -14
  16. package/dist/protocols/google-speech.d.ts +2 -32
  17. package/dist/protocols/google-speech.js +22 -60
  18. package/dist/protocols/google-transcription.d.ts +173 -0
  19. package/dist/protocols/google-transcription.js +138 -0
  20. package/dist/protocols/openai-images.js +4 -7
  21. package/dist/protocols/openai-transcription.d.ts +207 -0
  22. package/dist/protocols/openai-transcription.js +190 -0
  23. package/dist/protocols/utils/gemini-generate-content.d.ts +65 -0
  24. package/dist/protocols/utils/gemini-generate-content.js +65 -0
  25. package/dist/protocols/utils/media-input.d.ts +4 -0
  26. package/dist/protocols/utils/media-input.js +14 -0
  27. package/dist/providers/assemblyai.d.ts +25 -0
  28. package/dist/providers/assemblyai.js +29 -0
  29. package/dist/providers/deepgram.d.ts +5 -0
  30. package/dist/providers/deepgram.js +5 -2
  31. package/dist/providers/google.d.ts +5 -0
  32. package/dist/providers/google.js +3 -0
  33. package/dist/providers/index.d.ts +1 -0
  34. package/dist/providers/index.js +1 -0
  35. package/dist/providers/openai.d.ts +5 -0
  36. package/dist/providers/openai.js +4 -0
  37. package/dist/route/media-protocol.d.ts +15 -3
  38. package/dist/route/media-protocol.js +6 -0
  39. package/dist/route/media.d.ts +5 -2
  40. package/dist/route/media.js +62 -40
  41. package/dist/transcription-client.d.ts +28 -0
  42. package/dist/transcription-client.js +69 -0
  43. package/dist/transcription.d.ts +1504 -0
  44. package/dist/transcription.js +140 -0
  45. package/dist/utils/media-type.d.ts +1 -0
  46. package/dist/utils/media-type.js +14 -0
  47. package/dist/video-client.d.ts +1 -1
  48. package/dist/video-client.js +3 -6
  49. package/dist/video.d.ts +1 -15
  50. package/dist/video.js +2 -14
  51. 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
+ };
@@ -4,3 +4,4 @@
4
4
  */
5
5
  export declare const detectMediaType: (bytes: Uint8Array) => string | undefined;
6
6
  export declare const extensionMediaType: (path: string) => string | undefined;
7
+ export declare const mediaTypeExtension: (mediaType: string) => string | undefined;
@@ -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
+ };
@@ -1,5 +1,5 @@
1
1
  import { Context, Effect, Layer, Stream } from "effect";
2
- import type { AwaitOptions, Generation } from "./generation.js";
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";
@@ -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 { isObservation, responseEvents, } from "./video.js";
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) => events(generation, options)))),
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, type Event as GenerationEvent } from "./generation.js";
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([VideoQueuedEvent, VideoProgressEvent, VideoOutputEvent, VideoFinishEvent]).pipe(Schema.toTaggedUnion("type"));
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-20048",
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-20048",
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-20048",
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"