| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,4 +6,4 @@ | |||
| 6 | 6 | /// <reference path="../sst-env.d.ts" /> | |
| 7 | 7 | ||
| 8 | 8 | import "sst" | |
| 9 | - export {} | ||
| 9 | + export {} | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -116,6 +116,8 @@ const gatewayKv = new sst.cloudflare.Kv("GatewayKv") | |||
| 116 | 116 | // CONSOLE | |
| 117 | 117 | //////////////// | |
| 118 | 118 | ||
| 119 | + const bucket = new sst.cloudflare.Bucket("ConsoleData") | ||
| 120 | + | ||
| 119 | 121 | const AWS_SES_ACCESS_KEY_ID = new sst.Secret("AWS_SES_ACCESS_KEY_ID") | |
| 120 | 122 | const AWS_SES_SECRET_ACCESS_KEY = new sst.Secret("AWS_SES_SECRET_ACCESS_KEY") | |
| 121 | 123 | ||
@@ -132,6 +134,7 @@ new sst.cloudflare.x.SolidStart("Console", { | |||
| 132 | 134 | domain, | |
| 133 | 135 | path: "packages/console/app", | |
| 134 | 136 | link: [ | |
| 137 | + bucket, | ||
| 135 | 138 | database, | |
| 136 | 139 | AUTH_API_URL, | |
| 137 | 140 | STRIPE_WEBHOOK_SECRET, | |
| Original file line number | Diff line number | Diff 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 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,6 +19,7 @@ import { googleHelper } from "./provider/google" | |||
| 19 | 19 | import { openaiHelper } from "./provider/openai" | |
| 20 | 20 | import { oaCompatHelper } from "./provider/openai-compatible" | |
| 21 | 21 | import { createRateLimiter } from "./rateLimiter" | |
| 22 | + import { createDataDumper } from "./dataDumper" | ||
| 22 | 23 | ||
| 23 | 24 | type ZenData = Awaited<ReturnType<typeof ZenData.list>> | |
| 24 | 25 | type RetryOptions = { | |
@@ -48,16 +49,19 @@ export async function handler( | |||
| 48 | 49 | try { | |
| 49 | 50 | const url = input.request.url | |
| 50 | 51 | const body = await input.request.json() | |
| 51 | - const ip = input.request.headers.get("x-real-ip") ?? "" | ||
| 52 | 52 | const model = opts.parseModel(url, body) | |
| 53 | 53 | 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") | ||
| 54 | 57 | logger.metric({ | |
| 55 | 58 | 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, | ||
| 58 | 61 | }) | |
| 59 | 62 | const zenData = ZenData.list() | |
| 60 | 63 | const modelInfo = validateModel(zenData, model) | |
| 64 | + const dataDumper = createDataDumper(sessionId, requestId) | ||
| 61 | 65 | const rateLimiter = createRateLimiter(modelInfo.id, modelInfo.rateLimit, ip) | |
| 62 | 66 | await rateLimiter?.check() | |
| 63 | 67 | ||
@@ -104,10 +108,14 @@ export async function handler( | |||
| 104 | 108 | }) | |
| 105 | 109 | } | |
| 106 | 110 | ||
| 107 | - return { providerInfo, authInfo, res, startTimestamp } | ||
| 111 | + return { providerInfo, authInfo, reqBody, res, startTimestamp } | ||
| 108 | 112 | } | |
| 109 | 113 | ||
| 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) | ||
| 111 | 119 | ||
| 112 | 120 | // Scrub response headers | |
| 113 | 121 | const resHeaders = new Headers() | |
@@ -126,6 +134,8 @@ export async function handler( | |||
| 126 | 134 | const body = JSON.stringify(responseConverter(json)) | |
| 127 | 135 | logger.metric({ response_length: body.length }) | |
| 128 | 136 | logger.debug("RESPONSE: " + body) | |
| 137 | + dataDumper?.provideResponse(body) | ||
| 138 | + dataDumper?.flush() | ||
| 129 | 139 | await rateLimiter?.track() | |
| 130 | 140 | await trackUsage(authInfo, modelInfo, providerInfo, json.usage) | |
| 131 | 141 | await reload(authInfo) | |
@@ -155,6 +165,7 @@ export async function handler( | |||
| 155 | 165 | response_length: responseLength, | |
| 156 | 166 | "timestamp.last_byte": Date.now(), | |
| 157 | 167 | }) | |
| 168 | + dataDumper?.flush() | ||
| 158 | 169 | await rateLimiter?.track() | |
| 159 | 170 | const usage = usageParser.retrieve() | |
| 160 | 171 | if (usage) { | |
@@ -174,6 +185,7 @@ export async function handler( | |||
| 174 | 185 | } | |
| 175 | 186 | responseLength += value.length | |
| 176 | 187 | buffer += decoder.decode(value, { stream: true }) | |
| 188 | + dataDumper?.provideStream(buffer) | ||
| 177 | 189 | ||
| 178 | 190 | const parts = buffer.split(providerInfo.streamSeparator) | |
| 179 | 191 | buffer = parts.pop() ?? "" | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,4 +6,4 @@ | |||
| 6 | 6 | /// <reference path="../../../sst-env.d.ts" /> | |
| 7 | 7 | ||
| 8 | 8 | import "sst" | |
| 9 | - export {} | ||
| 9 | + export {} | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -32,6 +32,7 @@ export namespace ZenData { | |||
| 32 | 32 | model: z.string(), | |
| 33 | 33 | weight: z.number().optional(), | |
| 34 | 34 | disabled: z.boolean().optional(), | |
| 35 | + storeModel: z.string().optional(), | ||
| 35 | 36 | }), | |
| 36 | 37 | ), | |
| 37 | 38 | }) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,116 +6,126 @@ | |||
| 6 | 6 | import "sst" | |
| 7 | 7 | declare module "sst" { | |
| 8 | 8 | 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 | ||
| 104 | 112 | } | |
| 105 | 113 | } | |
| 106 | 114 | } | |
| 107 | - // cloudflare | ||
| 108 | - import * as cloudflare from "@cloudflare/workers-types" | ||
| 115 | + // cloudflare | ||
| 116 | + import * as cloudflare from "@cloudflare/workers-types"; | ||
| 109 | 117 | declare module "sst" { | |
| 110 | 118 | 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 | ||
| 117 | 127 | } | |
| 118 | 128 | } | |
| 119 | 129 | ||
| 120 | 130 | import "sst" | |
| 121 | - export {} | ||
| 131 | + export {} | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments