@opencode-ai/ai 0.0.0-next-16566 → 0.0.0-next-16569

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.
@@ -4,7 +4,7 @@ import { Endpoint, type EndpointPatch } from "./endpoint";
4
4
  import { RequestExecutor } from "./executor";
5
5
  import { Framing } from "./framing";
6
6
  import { HttpTransport } from "./transport";
7
- import type { Transport, TransportRuntime } from "./transport";
7
+ import type { HttpRequestTransform, Transport, TransportRuntime } from "./transport";
8
8
  import type { Protocol } from "./protocol";
9
9
  import type { LLMError, ProtocolID, ProviderOptions } from "../schema";
10
10
  import { GenerationOptions, HttpOptions, LLMRequest, LLMResponse, Model, ModelLimits, LLMEvent, ProviderID } from "../schema";
@@ -27,7 +27,7 @@ export interface Route<Body, Prepared = unknown> {
27
27
  readonly body: RouteBody<Body>;
28
28
  readonly with: (patch: RoutePatch<Body, Prepared>) => Route<Body, Prepared>;
29
29
  readonly model: <Options extends ProviderOptions = ProviderOptions>(input: RouteMappedModelInput) => Model<Options>;
30
- readonly prepareTransport: (body: Body, request: LLMRequest) => Effect.Effect<Prepared, LLMError>;
30
+ readonly prepareTransport: (body: Body, request: LLMRequest, options?: StreamOptions) => Effect.Effect<Prepared, LLMError>;
31
31
  readonly streamPrepared: (prepared: Prepared, request: LLMRequest, runtime: TransportRuntime) => Stream.Stream<LLMEvent, LLMError>;
32
32
  }
33
33
  export type AnyRoute = Route<any, any>;
@@ -62,11 +62,14 @@ export interface Interface {
62
62
  readonly stream: StreamMethod;
63
63
  readonly generate: GenerateMethod;
64
64
  }
65
+ export interface StreamOptions {
66
+ readonly transform?: HttpRequestTransform;
67
+ }
65
68
  export interface StreamMethod {
66
- (request: LLMRequest): Stream.Stream<LLMEvent, LLMError>;
69
+ (request: LLMRequest, options?: StreamOptions): Stream.Stream<LLMEvent, LLMError>;
67
70
  }
68
71
  export interface GenerateMethod {
69
- (request: LLMRequest): Effect.Effect<LLMResponse, LLMError>;
72
+ (request: LLMRequest, options?: StreamOptions): Effect.Effect<LLMResponse, LLMError>;
70
73
  }
71
74
  declare const Service_base: Context.ServiceClass<Service, "@opencode/LLMClient", Interface>;
72
75
  export declare class Service extends Service_base {
@@ -147,9 +150,9 @@ export declare const compileRequest: (request: LLMRequest) => Effect.Effect<{
147
150
  transport: string;
148
151
  };
149
152
  }, LLMError, never>;
150
- export declare function stream(request: LLMRequest): Stream.Stream<LLMEvent, LLMError>;
151
- export declare function generate(request: LLMRequest): Effect.Effect<LLMResponse, LLMError>;
152
- export declare const streamRequest: (request: LLMRequest) => Stream.Stream<{
153
+ export declare function stream(request: LLMRequest, options?: StreamOptions): Stream.Stream<LLMEvent, LLMError>;
154
+ export declare function generate(request: LLMRequest, options?: StreamOptions): Effect.Effect<LLMResponse, LLMError>;
155
+ export declare const streamRequest: (request: LLMRequest, options?: StreamOptions) => Stream.Stream<{
153
156
  readonly type: "step-start";
154
157
  readonly index: number;
155
158
  } | {
@@ -105,13 +105,14 @@ function makeFromTransport(input) {
105
105
  });
106
106
  },
107
107
  model: (input) => makeRouteModel(route, input),
108
- prepareTransport: (body, request) => routeInput.transport.prepare({
108
+ prepareTransport: (body, request, options) => routeInput.transport.prepare({
109
109
  body,
110
110
  request,
111
111
  endpoint: routeInput.endpoint,
112
112
  auth: routeInput.auth ?? Auth.none,
113
113
  encodeBody,
114
114
  headers: routeInput.headers,
115
+ transform: options?.transform,
115
116
  }),
116
117
  streamPrepared: (prepared, request, runtime) => {
117
118
  const route = `${request.model.provider}/${request.model.route.id}`;
@@ -141,13 +142,13 @@ export function make(input) {
141
142
  defaults: input.defaults,
142
143
  });
143
144
  }
144
- const compile = Effect.fn("LLM.compile")(function* (request) {
145
+ const compile = Effect.fn("LLM.compile")(function* (request, options) {
145
146
  const resolved = applyCachePolicy(resolveRequestOptions(request));
146
147
  const route = resolved.model.route;
147
148
  const body = yield* route.body
148
149
  .from(resolved)
149
150
  .pipe(Effect.flatMap(ProviderShared.validateWith(Schema.decodeUnknownEffect(route.body.schema))));
150
- const prepared = yield* route.prepareTransport(body, resolved);
151
+ const prepared = yield* route.prepareTransport(body, resolved, options);
151
152
  return {
152
153
  request: resolved,
153
154
  route,
@@ -167,29 +168,29 @@ export const compileRequest = Effect.fn("LLM.compileRequest")(function* (request
167
168
  metadata: { transport: compiled.route.transport.id },
168
169
  };
169
170
  });
170
- const streamRequestWith = (runtime) => (request) => Stream.unwrap(Effect.gen(function* () {
171
- const compiled = yield* compile(request);
171
+ const streamRequestWith = (runtime) => (request, options) => Stream.unwrap(Effect.gen(function* () {
172
+ const compiled = yield* compile(request, options);
172
173
  return compiled.route.streamPrepared(compiled.prepared, compiled.request, runtime);
173
174
  }));
174
- const generateWith = (stream) => Effect.fn("LLM.generate")(function* (request) {
175
- const state = yield* stream(request).pipe(Stream.runFold(LLMResponse.empty, LLMResponse.reduce));
175
+ const generateWith = (stream) => Effect.fn("LLM.generate")(function* (request, options) {
176
+ const state = yield* stream(request, options).pipe(Stream.runFold(LLMResponse.empty, LLMResponse.reduce));
176
177
  const response = LLMResponse.complete(state);
177
178
  if (response)
178
179
  return response;
179
180
  return yield* ProviderShared.eventError(`${request.model.provider}/${request.model.route.id}`, "Provider stream ended without a terminal finish event");
180
181
  });
181
- export function stream(request) {
182
+ export function stream(request, options) {
182
183
  return Stream.unwrap(Effect.gen(function* () {
183
- return (yield* Service).stream(request);
184
+ return (yield* Service).stream(request, options);
184
185
  }));
185
186
  }
186
- export function generate(request) {
187
+ export function generate(request, options) {
187
188
  return Effect.gen(function* () {
188
- return yield* (yield* Service).generate(request);
189
+ return yield* (yield* Service).generate(request, options);
189
190
  });
190
191
  }
191
- export const streamRequest = (request) => Stream.unwrap(Effect.gen(function* () {
192
- return (yield* Service).stream(request);
192
+ export const streamRequest = (request, options) => Stream.unwrap(Effect.gen(function* () {
193
+ return (yield* Service).stream(request, options);
193
194
  }));
194
195
  export const layer = Layer.effect(Service, Effect.gen(function* () {
195
196
  const stream = streamRequestWith({
@@ -1,5 +1,5 @@
1
1
  export { Route, LLMClient } from "./client";
2
- export type { Route as RouteShape, RouteModelInput, RouteRoutedModelInput, RouteDefaults, RouteDefaultsInput, AnyRoute, Interface as LLMClientShape, Service as LLMClientService, } from "./client";
2
+ export type { Route as RouteShape, RouteModelInput, RouteRoutedModelInput, RouteDefaults, RouteDefaultsInput, AnyRoute, Interface as LLMClientShape, Service as LLMClientService, StreamOptions, } from "./client";
3
3
  export * from "./executor";
4
4
  export { Auth } from "./auth";
5
5
  export { AuthOptions } from "./auth-options";
@@ -13,4 +13,4 @@ export type { ApiKeyMode, AuthOverride, ProviderAuthOption } from "./auth-option
13
13
  export type { Definition as EndpointFn, EndpointInput } from "./endpoint";
14
14
  export type { Definition as FramingDef } from "./framing";
15
15
  export type { Protocol as ProtocolDef } from "./protocol";
16
- export type { Transport as TransportDef, TransportRuntime } from "./transport";
16
+ export type { HttpRequest, HttpRequestTransform, Transport as TransportDef, TransportRuntime } from "./transport";
@@ -82,12 +82,19 @@ export const jsonRequestParts = (input) => Effect.gen(function* () {
82
82
  export const httpJson = (input) => ({
83
83
  id: "http-json",
84
84
  with: (patch) => httpJson({ ...input, ...patch }),
85
- prepare: (prepareInput) => jsonRequestParts({
86
- ...prepareInput,
87
- }).pipe(Effect.map((parts) => ({
88
- request: ProviderShared.jsonPost({ url: parts.url, body: parts.bodyText, headers: parts.headers }),
89
- framing: input.framing,
90
- }))),
85
+ prepare: (prepareInput) => Effect.gen(function* () {
86
+ const parts = yield* jsonRequestParts({ ...prepareInput });
87
+ const request = { url: parts.url, method: "POST", headers: { ...parts.headers }, body: parts.bodyText };
88
+ yield* (prepareInput.transform?.(request) ?? Effect.void);
89
+ return {
90
+ request: ProviderShared.jsonPost({
91
+ url: request.url,
92
+ body: request.body ?? "",
93
+ headers: Headers.fromInput(request.headers),
94
+ }),
95
+ framing: input.framing,
96
+ };
97
+ }),
91
98
  frames: (prepared, request, runtime) => Stream.unwrap(runtime.http
92
99
  .execute(prepared.request)
93
100
  .pipe(Effect.map((response) => prepared.framing.frame(response.stream.pipe(Stream.mapError((error) => ProviderShared.eventError(`${request.model.provider}/${request.model.route.id}`, `Failed to read ${request.model.provider}/${request.model.route.id} stream`, ProviderShared.errorText(error)))))))),
@@ -8,6 +8,13 @@ export interface TransportRuntime {
8
8
  readonly http: RequestExecutorInterface;
9
9
  readonly webSocket?: WebSocketExecutorInterface;
10
10
  }
11
+ export interface HttpRequest {
12
+ url: string;
13
+ readonly method: string;
14
+ headers: Record<string, string>;
15
+ body: string | undefined;
16
+ }
17
+ export type HttpRequestTransform = (request: HttpRequest) => Effect.Effect<void>;
11
18
  export interface Transport<Body, Prepared, Frame> {
12
19
  readonly id: string;
13
20
  readonly prepare: (input: TransportPrepareInput<Body>) => Effect.Effect<Prepared, LLMError>;
@@ -22,6 +29,7 @@ export interface TransportPrepareInput<Body> {
22
29
  readonly headers?: (input: {
23
30
  readonly request: LLMRequest;
24
31
  }) => Record<string, string>;
32
+ readonly transform?: HttpRequestTransform;
25
33
  }
26
34
  export * as HttpTransport from "./http";
27
35
  export { WebSocketExecutor, WebSocketTransport } from "./websocket";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-next-16566",
3
+ "version": "0.0.0-next-16569",
4
4
  "name": "@opencode-ai/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,7 +30,7 @@
30
30
  "devDependencies": {
31
31
  "@clack/prompts": "1.0.0-alpha.1",
32
32
  "@effect/platform-node": "4.0.0-beta.101",
33
- "@opencode-ai/http-recorder": "0.0.0-next-16566",
33
+ "@opencode-ai/http-recorder": "0.0.0-next-16569",
34
34
  "@tsconfig/bun": "1.0.9",
35
35
  "@types/bun": "1.3.13",
36
36
  "@typescript/native-preview": "7.0.0-dev.20251207.1",
@@ -39,7 +39,7 @@
39
39
  "dependencies": {
40
40
  "@smithy/eventstream-codec": "4.2.14",
41
41
  "@smithy/util-utf8": "4.2.2",
42
- "@opencode-ai/schema": "0.0.0-next-16566",
42
+ "@opencode-ai/schema": "0.0.0-next-16569",
43
43
  "aws4fetch": "1.0.20",
44
44
  "effect": "4.0.0-beta.101",
45
45
  "google-auth-library": "10.5.0"