Skip to content

Commit bbc9142

Browse files
author
Frank
committed
wip: zen
1 parent 7413c27 commit bbc9142

File tree

30 files changed

+837
-572
lines changed

30 files changed

+837
-572
lines changed

github/sst-env.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
/// <reference path="../sst-env.d.ts" />
77

88
import "sst"
9-
export {}
9+
export {}

infra/console.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ const gatewayKv = new sst.cloudflare.Kv("GatewayKv")
116116
// CONSOLE
117117
////////////////
118118

119+
const bucket = new sst.cloudflare.Bucket("ConsoleData")
120+
119121
const AWS_SES_ACCESS_KEY_ID = new sst.Secret("AWS_SES_ACCESS_KEY_ID")
120122
const AWS_SES_SECRET_ACCESS_KEY = new sst.Secret("AWS_SES_SECRET_ACCESS_KEY")
121123

@@ -132,6 +134,7 @@ new sst.cloudflare.x.SolidStart("Console", {
132134
domain,
133135
path: "packages/console/app",
134136
link: [
137+
bucket,
135138
database,
136139
AUTH_API_URL,
137140
STRIPE_WEBHOOK_SECRET,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Resource, waitUntil } from "@opencode-ai/console-resource"
2+
3+
export function createDataDumper(sessionId: string, requestId: string) {
4+
if (Resource.App.stage !== "production") return
5+
6+
let data: Record<string, any> = {}
7+
let modelName: string | undefined
8+
9+
return {
10+
provideModel: (model?: string) => (modelName = model),
11+
provideRequest: (request: string) => (data.request = request),
12+
provideResponse: (response: string) => (data.response = response),
13+
provideStream: (chunk: string) => (data.response = (data.response ?? "") + chunk),
14+
flush: () => {
15+
if (!modelName) return
16+
17+
const str = new Date().toISOString().replace(/[^0-9]/g, "")
18+
const yyyymmdd = str.substring(0, 8)
19+
const hh = str.substring(8, 10)
20+
21+
waitUntil(
22+
Resource.ConsoleData.put(`${yyyymmdd}/${hh}/${modelName}/${sessionId}/${requestId}.json`, JSON.stringify(data)),
23+
)
24+
},
25+
}
26+
}

packages/console/app/src/routes/zen/util/handler.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { googleHelper } from "./provider/google"
1919
import { openaiHelper } from "./provider/openai"
2020
import { oaCompatHelper } from "./provider/openai-compatible"
2121
import { createRateLimiter } from "./rateLimiter"
22+
import { createDataDumper } from "./dataDumper"
2223

2324
type ZenData = Awaited<ReturnType<typeof ZenData.list>>
2425
type RetryOptions = {
@@ -48,16 +49,19 @@ export async function handler(
4849
try {
4950
const url = input.request.url
5051
const body = await input.request.json()
51-
const ip = input.request.headers.get("x-real-ip") ?? ""
5252
const model = opts.parseModel(url, body)
5353
const isStream = opts.parseIsStream(url, body)
54+
const ip = input.request.headers.get("x-real-ip") ?? ""
55+
const sessionId = input.request.headers.get("x-opencode-session")
56+
const requestId = input.request.headers.get("x-opencode-request")
5457
logger.metric({
5558
is_tream: isStream,
56-
session: input.request.headers.get("x-opencode-session"),
57-
request: input.request.headers.get("x-opencode-request"),
59+
session: sessionId,
60+
request: requestId,
5861
})
5962
const zenData = ZenData.list()
6063
const modelInfo = validateModel(zenData, model)
64+
const dataDumper = createDataDumper(sessionId, requestId)
6165
const rateLimiter = createRateLimiter(modelInfo.id, modelInfo.rateLimit, ip)
6266
await rateLimiter?.check()
6367

@@ -104,10 +108,14 @@ export async function handler(
104108
})
105109
}
106110

107-
return { providerInfo, authInfo, res, startTimestamp }
111+
return { providerInfo, authInfo, reqBody, res, startTimestamp }
108112
}
109113

110-
const { providerInfo, authInfo, res, startTimestamp } = await retriableRequest()
114+
const { providerInfo, authInfo, reqBody, res, startTimestamp } = await retriableRequest()
115+
116+
// Store model request
117+
dataDumper?.provideModel(providerInfo.storeModel)
118+
dataDumper?.provideRequest(reqBody)
111119

112120
// Scrub response headers
113121
const resHeaders = new Headers()
@@ -126,6 +134,8 @@ export async function handler(
126134
const body = JSON.stringify(responseConverter(json))
127135
logger.metric({ response_length: body.length })
128136
logger.debug("RESPONSE: " + body)
137+
dataDumper?.provideResponse(body)
138+
dataDumper?.flush()
129139
await rateLimiter?.track()
130140
await trackUsage(authInfo, modelInfo, providerInfo, json.usage)
131141
await reload(authInfo)
@@ -155,6 +165,7 @@ export async function handler(
155165
response_length: responseLength,
156166
"timestamp.last_byte": Date.now(),
157167
})
168+
dataDumper?.flush()
158169
await rateLimiter?.track()
159170
const usage = usageParser.retrieve()
160171
if (usage) {
@@ -174,6 +185,7 @@ export async function handler(
174185
}
175186
responseLength += value.length
176187
buffer += decoder.decode(value, { stream: true })
188+
dataDumper?.provideStream(buffer)
177189

178190
const parts = buffer.split(providerInfo.streamSeparator)
179191
buffer = parts.pop() ?? ""

packages/console/app/sst-env.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
/// <reference path="../../../sst-env.d.ts" />
77

88
import "sst"
9-
export {}
9+
export {}

packages/console/core/src/model.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export namespace ZenData {
3232
model: z.string(),
3333
weight: z.number().optional(),
3434
disabled: z.boolean().optional(),
35+
storeModel: z.string().optional(),
3536
}),
3637
),
3738
})

packages/console/core/sst-env.d.ts

Lines changed: 114 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -6,116 +6,126 @@
66
import "sst"
77
declare module "sst" {
88
export interface Resource {
9-
ADMIN_SECRET: {
10-
type: "sst.sst.Secret"
11-
value: string
12-
}
13-
AUTH_API_URL: {
14-
type: "sst.sst.Linkable"
15-
value: string
16-
}
17-
AWS_SES_ACCESS_KEY_ID: {
18-
type: "sst.sst.Secret"
19-
value: string
20-
}
21-
AWS_SES_SECRET_ACCESS_KEY: {
22-
type: "sst.sst.Secret"
23-
value: string
24-
}
25-
CLOUDFLARE_API_TOKEN: {
26-
type: "sst.sst.Secret"
27-
value: string
28-
}
29-
CLOUDFLARE_DEFAULT_ACCOUNT_ID: {
30-
type: "sst.sst.Secret"
31-
value: string
32-
}
33-
Console: {
34-
type: "sst.cloudflare.SolidStart"
35-
url: string
36-
}
37-
Database: {
38-
database: string
39-
host: string
40-
password: string
41-
port: number
42-
type: "sst.sst.Linkable"
43-
username: string
44-
}
45-
Desktop: {
46-
type: "sst.cloudflare.StaticSite"
47-
url: string
48-
}
49-
EMAILOCTOPUS_API_KEY: {
50-
type: "sst.sst.Secret"
51-
value: string
52-
}
53-
GITHUB_APP_ID: {
54-
type: "sst.sst.Secret"
55-
value: string
56-
}
57-
GITHUB_APP_PRIVATE_KEY: {
58-
type: "sst.sst.Secret"
59-
value: string
60-
}
61-
GITHUB_CLIENT_ID_CONSOLE: {
62-
type: "sst.sst.Secret"
63-
value: string
64-
}
65-
GITHUB_CLIENT_SECRET_CONSOLE: {
66-
type: "sst.sst.Secret"
67-
value: string
68-
}
69-
GOOGLE_CLIENT_ID: {
70-
type: "sst.sst.Secret"
71-
value: string
72-
}
73-
HONEYCOMB_API_KEY: {
74-
type: "sst.sst.Secret"
75-
value: string
76-
}
77-
STRIPE_SECRET_KEY: {
78-
type: "sst.sst.Secret"
79-
value: string
80-
}
81-
STRIPE_WEBHOOK_SECRET: {
82-
type: "sst.sst.Linkable"
83-
value: string
84-
}
85-
Web: {
86-
type: "sst.cloudflare.Astro"
87-
url: string
88-
}
89-
ZEN_MODELS1: {
90-
type: "sst.sst.Secret"
91-
value: string
92-
}
93-
ZEN_MODELS2: {
94-
type: "sst.sst.Secret"
95-
value: string
96-
}
97-
ZEN_MODELS3: {
98-
type: "sst.sst.Secret"
99-
value: string
100-
}
101-
ZEN_MODELS4: {
102-
type: "sst.sst.Secret"
103-
value: string
9+
"ADMIN_SECRET": {
10+
"type": "sst.sst.Secret"
11+
"value": string
12+
}
13+
"AUTH_API_URL": {
14+
"type": "sst.sst.Linkable"
15+
"value": string
16+
}
17+
"AWS_SES_ACCESS_KEY_ID": {
18+
"type": "sst.sst.Secret"
19+
"value": string
20+
}
21+
"AWS_SES_SECRET_ACCESS_KEY": {
22+
"type": "sst.sst.Secret"
23+
"value": string
24+
}
25+
"CLOUDFLARE_API_TOKEN": {
26+
"type": "sst.sst.Secret"
27+
"value": string
28+
}
29+
"CLOUDFLARE_DEFAULT_ACCOUNT_ID": {
30+
"type": "sst.sst.Secret"
31+
"value": string
32+
}
33+
"Console": {
34+
"type": "sst.cloudflare.SolidStart"
35+
"url": string
36+
}
37+
"Database": {
38+
"database": string
39+
"host": string
40+
"password": string
41+
"port": number
42+
"type": "sst.sst.Linkable"
43+
"username": string
44+
}
45+
"Desktop": {
46+
"type": "sst.cloudflare.StaticSite"
47+
"url": string
48+
}
49+
"EMAILOCTOPUS_API_KEY": {
50+
"type": "sst.sst.Secret"
51+
"value": string
52+
}
53+
"GITHUB_APP_ID": {
54+
"type": "sst.sst.Secret"
55+
"value": string
56+
}
57+
"GITHUB_APP_PRIVATE_KEY": {
58+
"type": "sst.sst.Secret"
59+
"value": string
60+
}
61+
"GITHUB_CLIENT_ID_CONSOLE": {
62+
"type": "sst.sst.Secret"
63+
"value": string
64+
}
65+
"GITHUB_CLIENT_SECRET_CONSOLE": {
66+
"type": "sst.sst.Secret"
67+
"value": string
68+
}
69+
"GOOGLE_CLIENT_ID": {
70+
"type": "sst.sst.Secret"
71+
"value": string
72+
}
73+
"HONEYCOMB_API_KEY": {
74+
"type": "sst.sst.Secret"
75+
"value": string
76+
}
77+
"R2AccessKey": {
78+
"type": "sst.sst.Secret"
79+
"value": string
80+
}
81+
"R2SecretKey": {
82+
"type": "sst.sst.Secret"
83+
"value": string
84+
}
85+
"STRIPE_SECRET_KEY": {
86+
"type": "sst.sst.Secret"
87+
"value": string
88+
}
89+
"STRIPE_WEBHOOK_SECRET": {
90+
"type": "sst.sst.Linkable"
91+
"value": string
92+
}
93+
"Web": {
94+
"type": "sst.cloudflare.Astro"
95+
"url": string
96+
}
97+
"ZEN_MODELS1": {
98+
"type": "sst.sst.Secret"
99+
"value": string
100+
}
101+
"ZEN_MODELS2": {
102+
"type": "sst.sst.Secret"
103+
"value": string
104+
}
105+
"ZEN_MODELS3": {
106+
"type": "sst.sst.Secret"
107+
"value": string
108+
}
109+
"ZEN_MODELS4": {
110+
"type": "sst.sst.Secret"
111+
"value": string
104112
}
105113
}
106114
}
107-
// cloudflare
108-
import * as cloudflare from "@cloudflare/workers-types"
115+
// cloudflare
116+
import * as cloudflare from "@cloudflare/workers-types";
109117
declare module "sst" {
110118
export interface Resource {
111-
Api: cloudflare.Service
112-
AuthApi: cloudflare.Service
113-
AuthStorage: cloudflare.KVNamespace
114-
Bucket: cloudflare.R2Bucket
115-
GatewayKv: cloudflare.KVNamespace
116-
LogProcessor: cloudflare.Service
119+
"Api": cloudflare.Service
120+
"AuthApi": cloudflare.Service
121+
"AuthStorage": cloudflare.KVNamespace
122+
"Bucket": cloudflare.R2Bucket
123+
"ConsoleData": cloudflare.R2Bucket
124+
"EnterpriseStorage": cloudflare.R2Bucket
125+
"GatewayKv": cloudflare.KVNamespace
126+
"LogProcessor": cloudflare.Service
117127
}
118128
}
119129

120130
import "sst"
121-
export {}
131+
export {}

0 commit comments

Comments
 (0)