Compare commits
2 commits
dev
...
gpt56-stre
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
917d18203a | ||
|
|
3a0e9efe7d |
68 changed files with 225 additions and 4033 deletions
3
.github/workflows/publish.yml
vendored
3
.github/workflows/publish.yml
vendored
|
|
@ -339,7 +339,8 @@ jobs:
|
||||||
env:
|
env:
|
||||||
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
|
OPENCODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }}
|
||||||
GH_TOKEN: ${{ steps.committer.outputs.token }}
|
GH_TOKEN: ${{ steps.committer.outputs.token }}
|
||||||
CSC_KEYCHAIN: build.keychain
|
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
|
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
APPLE_API_KEY: ${{ runner.temp }}/apple-api-key.p8
|
APPLE_API_KEY: ${{ runner.temp }}/apple-api-key.p8
|
||||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY }}
|
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY }}
|
||||||
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
|
||||||
|
|
|
||||||
1
bun.lock
1
bun.lock
|
|
@ -384,7 +384,6 @@
|
||||||
"version": "1.18.11",
|
"version": "1.18.11",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@zip.js/zip.js": "2.7.62",
|
"@zip.js/zip.js": "2.7.62",
|
||||||
"drizzle-orm": "catalog:",
|
|
||||||
"effect": "catalog:",
|
"effect": "catalog:",
|
||||||
"electron-context-menu": "4.1.2",
|
"electron-context-menu": "4.1.2",
|
||||||
"electron-log": "^5",
|
"electron-log": "^5",
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"nodeModules": {
|
"nodeModules": {
|
||||||
"x86_64-linux": "sha256-a7NyYa9vRUEqDfZNDPXXmFO58RDEgioyuGSl5CPBvxo=",
|
"x86_64-linux": "sha256-SiJd6uXrL/MqqFGN/uUcHh0Wzdlafnpx++VZa5gUCoE=",
|
||||||
"aarch64-linux": "sha256-l4OJtSEllHvRhktjcaJYwkXBSaJvsIoyoLusbZfYMcM=",
|
"aarch64-linux": "sha256-pKcT34NYIHVasraRTx0ASTzyuFuIzBTxXg6+KSKvTps=",
|
||||||
"aarch64-darwin": "sha256-IIl0BQGs1/HLFh0auiQjiwfSQ2nfHcK2G2BAphYW59c=",
|
"aarch64-darwin": "sha256-hzrym0KpiyYAv80eT/DjcSIJWYBUq4QDgDf6Wtot7jU=",
|
||||||
"x86_64-darwin": "sha256-vVeuPyd4ZIRYrHouphTuEb4rkRZLKKTAHe840jNh9rU="
|
"x86_64-darwin": "sha256-Or5dSTdajUwij0XpovXSXjJ0LmRUcpcW5TJ4q4B0k0A="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
import { expect, test } from "@playwright/test"
|
|
||||||
import type { Page } from "@playwright/test"
|
|
||||||
import { fixture, pageMessages } from "../smoke/session-timeline.fixture"
|
|
||||||
import { mockOpenCodeServer } from "../utils/mock-server"
|
|
||||||
import { expectAppVisible } from "../utils/waits"
|
|
||||||
|
|
||||||
const NAMES = ["alpha-service", "bravo-web", "charlie-api", "delta-tools", "echo-infra", "foxtrot-docs"]
|
|
||||||
const worktrees = NAMES.map((name) => `/opencode-demo/${name}`)
|
|
||||||
|
|
||||||
// The sixth project sits outside the five-item recent cap, so it is only reachable if the
|
|
||||||
// dialog hands every recent project to the list filter instead of a pre-truncated slice.
|
|
||||||
const OUTSIDE_CAP = "foxtrot-docs"
|
|
||||||
|
|
||||||
// Dialog rows carry data-directory-path; the sidebar project list does not, so this
|
|
||||||
// scopes assertions to the picker instead of matching the sidebar entry of the same name.
|
|
||||||
const rows = (page: Page) => page.locator("[data-directory-path]")
|
|
||||||
const row = (page: Page, name: string) => page.locator(`[data-directory-path*="${name}"]`)
|
|
||||||
|
|
||||||
async function openProjectDialog(page: Page) {
|
|
||||||
await mockOpenCodeServer(page, {
|
|
||||||
sessions: fixture.sessions,
|
|
||||||
provider: fixture.provider,
|
|
||||||
directory: fixture.directory,
|
|
||||||
project: fixture.project,
|
|
||||||
pageMessages,
|
|
||||||
fileList: () => [],
|
|
||||||
findFiles: () => [],
|
|
||||||
})
|
|
||||||
await page.addInitScript((dirs) => {
|
|
||||||
localStorage.setItem(
|
|
||||||
"opencode.global.dat:server",
|
|
||||||
JSON.stringify({
|
|
||||||
projects: { local: dirs.map((worktree: string) => ({ worktree, expanded: false })) },
|
|
||||||
lastProject: {},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
}, worktrees)
|
|
||||||
await page.goto("/")
|
|
||||||
const add = page.getByRole("button", { name: "Add project" }).first()
|
|
||||||
await expectAppVisible(add)
|
|
||||||
await add.click()
|
|
||||||
await expect(rows(page)).toHaveCount(5)
|
|
||||||
return page.getByRole("textbox").last()
|
|
||||||
}
|
|
||||||
|
|
||||||
test("searches every recent project, not just the five most recent", async ({ page }) => {
|
|
||||||
const search = await openProjectDialog(page)
|
|
||||||
await expect(row(page, OUTSIDE_CAP)).toHaveCount(0)
|
|
||||||
|
|
||||||
await search.fill("foxtrot")
|
|
||||||
|
|
||||||
await expect(row(page, OUTSIDE_CAP)).toHaveCount(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("still caps the idle recent list at five projects", async ({ page }) => {
|
|
||||||
await openProjectDialog(page)
|
|
||||||
|
|
||||||
await expect(row(page, NAMES[4])).toHaveCount(1)
|
|
||||||
await expect(row(page, OUTSIDE_CAP)).toHaveCount(0)
|
|
||||||
})
|
|
||||||
|
|
@ -18,8 +18,6 @@ interface DialogSelectDirectoryProps {
|
||||||
server: ServerConnection.Any
|
server: ServerConnection.Any
|
||||||
}
|
}
|
||||||
|
|
||||||
const RECENT_PROJECT_LIMIT = 5
|
|
||||||
|
|
||||||
type Row = {
|
type Row = {
|
||||||
absolute: string
|
absolute: string
|
||||||
search: string
|
search: string
|
||||||
|
|
@ -104,6 +102,7 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
|
||||||
return projects
|
return projects
|
||||||
.map((project, index) => ({ project, at: byProject.get(project.worktree) ?? 0, index }))
|
.map((project, index) => ({ project, at: byProject.get(project.worktree) ?? 0, index }))
|
||||||
.sort((a, b) => b.at - a.at || a.index - b.index)
|
.sort((a, b) => b.at - a.at || a.index - b.index)
|
||||||
|
.slice(0, 5)
|
||||||
.map(({ project }) => {
|
.map(({ project }) => {
|
||||||
const row = toRow(project.worktree, home(), "recent")
|
const row = toRow(project.worktree, home(), "recent")
|
||||||
const name = project.name || getFilename(project.worktree)
|
const name = project.name || getFilename(project.worktree)
|
||||||
|
|
@ -117,10 +116,7 @@ export function DialogSelectDirectory(props: DialogSelectDirectoryProps) {
|
||||||
const items = async (value: string) => {
|
const items = async (value: string) => {
|
||||||
const results = await directories(value)
|
const results = await directories(value)
|
||||||
const directoryRows = results.map((absolute) => toRow(absolute, home(), "folders"))
|
const directoryRows = results.map((absolute) => toRow(absolute, home(), "folders"))
|
||||||
// Cap the idle list only. Once a query narrows the results, every project stays searchable.
|
return uniqueRows([...recentProjects(), ...directoryRows])
|
||||||
const recent = recentProjects()
|
|
||||||
const visible = value ? recent : recent.slice(0, RECENT_PROJECT_LIMIT)
|
|
||||||
return uniqueRows([...visible, ...directoryRows])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolve(absolute: string) {
|
function resolve(absolute: string) {
|
||||||
|
|
|
||||||
|
|
@ -344,7 +344,7 @@ export function usePromptInputV2Controller(props: PromptInputV2ControllerProps):
|
||||||
if (item?.commentID) comments.remove(item.path, item.commentID)
|
if (item?.commentID) comments.remove(item.path, item.commentID)
|
||||||
},
|
},
|
||||||
openAttachment: (attachment) =>
|
openAttachment: (attachment) =>
|
||||||
dialog.show(() => <ImagePreview src={attachment.blob.url} alt={attachment.filename} />),
|
dialog.show(() => <ImagePreview src={attachment.dataUrl} alt={attachment.filename} />),
|
||||||
openContext(key) {
|
openContext(key) {
|
||||||
const item = controller.contextItem(key)
|
const item = controller.contextItem(key)
|
||||||
if (item) openComment(item, props, sync, layout, files, comments)
|
if (item) openComment(item, props, sync, layout, files, comments)
|
||||||
|
|
@ -377,7 +377,6 @@ export function usePromptInputV2Controller(props: PromptInputV2ControllerProps):
|
||||||
}),
|
}),
|
||||||
readClipboardImage: platform.readClipboardImage,
|
readClipboardImage: platform.readClipboardImage,
|
||||||
getPathForFile: platform.getPathForFile,
|
getPathForFile: platform.getPathForFile,
|
||||||
store: platform.draftStore?.putBlob,
|
|
||||||
},
|
},
|
||||||
view: {
|
view: {
|
||||||
placeholder: designPlaceholder,
|
placeholder: designPlaceholder,
|
||||||
|
|
|
||||||
|
|
@ -1489,7 +1489,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => {
|
||||||
<PromptImageAttachments
|
<PromptImageAttachments
|
||||||
attachments={imageAttachments()}
|
attachments={imageAttachments()}
|
||||||
onOpen={(attachment) =>
|
onOpen={(attachment) =>
|
||||||
dialog.show(() => <ImagePreview src={attachment.blob.url} alt={attachment.filename} />)
|
dialog.show(() => <ImagePreview src={attachment.dataUrl} alt={attachment.filename} />)
|
||||||
}
|
}
|
||||||
onRemove={removeAttachment}
|
onRemove={removeAttachment}
|
||||||
removeLabel={language.t("prompt.attachment.remove")}
|
removeLabel={language.t("prompt.attachment.remove")}
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,28 @@ import { makeEventListener } from "@solid-primitives/event-listener"
|
||||||
import { showToast } from "@/utils/toast"
|
import { showToast } from "@/utils/toast"
|
||||||
import { type ContentPart, type ImageAttachmentPart, type usePrompt } from "@/context/prompt"
|
import { type ContentPart, type ImageAttachmentPart, type usePrompt } from "@/context/prompt"
|
||||||
import { useLanguage } from "@/context/language"
|
import { useLanguage } from "@/context/language"
|
||||||
import { usePlatform } from "@/context/platform"
|
|
||||||
import { uuid } from "@/utils/uuid"
|
import { uuid } from "@/utils/uuid"
|
||||||
import { getCursorPosition } from "./editor-dom"
|
import { getCursorPosition } from "./editor-dom"
|
||||||
import { createBlobReference, type DraftStore } from "@/utils/draft-store"
|
|
||||||
import { attachmentMime } from "./files"
|
import { attachmentMime } from "./files"
|
||||||
import { normalizePaste, pasteMode } from "./paste"
|
import { normalizePaste, pasteMode } from "./paste"
|
||||||
|
|
||||||
|
function dataUrl(file: File, mime: string) {
|
||||||
|
return new Promise<string>((resolve) => {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.addEventListener("error", () => resolve(""))
|
||||||
|
reader.addEventListener("load", () => {
|
||||||
|
const value = typeof reader.result === "string" ? reader.result : ""
|
||||||
|
const idx = value.indexOf(",")
|
||||||
|
if (idx === -1) {
|
||||||
|
resolve(value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resolve(`data:${mime};base64,${value.slice(idx + 1)}`)
|
||||||
|
})
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
type PromptTarget = Pick<ReturnType<ReturnType<typeof usePrompt>["capture"]>, "current" | "cursor" | "set">
|
type PromptTarget = Pick<ReturnType<ReturnType<typeof usePrompt>["capture"]>, "current" | "cursor" | "set">
|
||||||
type AttachmentTarget = { prompt: PromptTarget; cursor: number | undefined }
|
type AttachmentTarget = { prompt: PromptTarget; cursor: number | undefined }
|
||||||
|
|
||||||
|
|
@ -21,7 +36,6 @@ type PromptAttachmentsCoreInput = {
|
||||||
warn?: () => void
|
warn?: () => void
|
||||||
readClipboardImage?: () => Promise<File | null>
|
readClipboardImage?: () => Promise<File | null>
|
||||||
getPathForFile?: (file: File) => string
|
getPathForFile?: (file: File) => string
|
||||||
draftStore?: DraftStore
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PromptAttachmentsInput = {
|
export type PromptAttachmentsInput = {
|
||||||
|
|
@ -51,13 +65,16 @@ export function createPromptAttachmentsCore(input: PromptAttachmentsCoreInput) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const url = await dataUrl(file, mime)
|
||||||
|
if (!url) return false
|
||||||
|
|
||||||
const attachment: ImageAttachmentPart = {
|
const attachment: ImageAttachmentPart = {
|
||||||
type: "image",
|
type: "image",
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
filename: file.name,
|
filename: file.name,
|
||||||
sourcePath: input.getPathForFile?.(file) || undefined,
|
sourcePath: input.getPathForFile?.(file) || undefined,
|
||||||
mime,
|
mime,
|
||||||
blob: input.draftStore ? await input.draftStore.putBlob(file) : await createBlobReference(file),
|
dataUrl: url,
|
||||||
}
|
}
|
||||||
target.prompt.set([...target.prompt.current(), attachment], target.cursor)
|
target.prompt.set([...target.prompt.current(), attachment], target.cursor)
|
||||||
return true
|
return true
|
||||||
|
|
@ -149,10 +166,8 @@ export function createPromptAttachmentsCore(input: PromptAttachmentsCoreInput) {
|
||||||
|
|
||||||
export function createPromptAttachments(input: PromptAttachmentsInput) {
|
export function createPromptAttachments(input: PromptAttachmentsInput) {
|
||||||
const language = useLanguage()
|
const language = useLanguage()
|
||||||
const platform = usePlatform()
|
|
||||||
const attachments = createPromptAttachmentsCore({
|
const attachments = createPromptAttachmentsCore({
|
||||||
...input,
|
...input,
|
||||||
draftStore: platform.draftStore,
|
|
||||||
capture: input.prompt.capture,
|
capture: input.prompt.capture,
|
||||||
warn: () => {
|
warn: () => {
|
||||||
showToast({
|
showToast({
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ type ContextFile = {
|
||||||
type BuildRequestPartsInput = {
|
type BuildRequestPartsInput = {
|
||||||
prompt: Prompt
|
prompt: Prompt
|
||||||
context: ContextFile[]
|
context: ContextFile[]
|
||||||
images: (Omit<ImageAttachmentPart, "blob"> & { dataUrl: string })[]
|
images: ImageAttachmentPart[]
|
||||||
text: string
|
text: string
|
||||||
messageID: string
|
messageID: string
|
||||||
sessionID: string
|
sessionID: string
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,7 @@
|
||||||
import { createStore, type SetStoreFunction, type Store } from "solid-js/store"
|
import { createStore, type SetStoreFunction, type Store } from "solid-js/store"
|
||||||
import type { Prompt } from "@/context/prompt"
|
import type { Prompt } from "@/context/prompt"
|
||||||
import { Persist, persisted } from "@/utils/persist"
|
import { Persist, persisted } from "@/utils/persist"
|
||||||
import {
|
import { prependHistoryEntry, type PromptHistoryComment, type PromptHistoryStoredEntry } from "./history"
|
||||||
clonePromptHistoryComments,
|
|
||||||
clonePromptParts,
|
|
||||||
prependHistoryEntry,
|
|
||||||
type PromptHistoryComment,
|
|
||||||
type PromptHistoryStoredEntry,
|
|
||||||
} from "./history"
|
|
||||||
|
|
||||||
export type PromptInputHistory = {
|
export type PromptInputHistory = {
|
||||||
entries: (mode: "normal" | "shell") => PromptHistoryStoredEntry[]
|
entries: (mode: "normal" | "shell") => PromptHistoryStoredEntry[]
|
||||||
|
|
@ -41,23 +35,13 @@ export function createPromptInputHistory(): PromptInputHistory {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPersistedPromptInputHistory() {
|
export function createPersistedPromptInputHistory() {
|
||||||
const [normal, setNormal, normalInit] = persisted(
|
const [normal, setNormal] = persisted(
|
||||||
Persist.prompt(Persist.global("prompt-history", ["prompt-history.v1"])),
|
Persist.global("prompt-history", ["prompt-history.v1"]),
|
||||||
createStore<PromptHistoryState>({ entries: [] }),
|
createStore<PromptHistoryState>({ entries: [] }),
|
||||||
)
|
)
|
||||||
const [shell, setShell, shellInit] = persisted(
|
const [shell, setShell] = persisted(
|
||||||
Persist.prompt(Persist.global("prompt-history-shell", ["prompt-history-shell.v1"])),
|
Persist.global("prompt-history-shell", ["prompt-history-shell.v1"]),
|
||||||
createStore<PromptHistoryState>({ entries: [] }),
|
createStore<PromptHistoryState>({ entries: [] }),
|
||||||
)
|
)
|
||||||
const history = createPromptInputHistoryStore(normal, setNormal, shell, setShell)
|
return createPromptInputHistoryStore(normal, setNormal, shell, setShell)
|
||||||
return {
|
|
||||||
...history,
|
|
||||||
add(prompt: Prompt, mode: "normal" | "shell", comments: PromptHistoryComment[]) {
|
|
||||||
const ready = mode === "shell" ? shellInit : normalInit
|
|
||||||
if (!(ready instanceof Promise)) return history.add(prompt, mode, comments)
|
|
||||||
const saved = clonePromptParts(prompt)
|
|
||||||
const metadata = clonePromptHistoryComments(comments)
|
|
||||||
void ready.then(() => history.add(saved, mode, metadata))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ describe("prompt-input history", () => {
|
||||||
end: 12,
|
end: 12,
|
||||||
selection: { startLine: 1, startChar: 1, endLine: 2, endChar: 1 },
|
selection: { startLine: 1, startChar: 1, endLine: 2, endChar: 1 },
|
||||||
},
|
},
|
||||||
{ type: "image", id: "1", filename: "img.png", mime: "image/png", blob: { id: "blob", url: "blob:test" } },
|
{ type: "image", id: "1", filename: "img.png", mime: "image/png", dataUrl: "data:image/png;base64,abc" },
|
||||||
]
|
]
|
||||||
const copy = clonePromptParts(original)
|
const copy = clonePromptParts(original)
|
||||||
expect(copy).not.toBe(original)
|
expect(copy).not.toBe(original)
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ export const PromptImageAttachments: Component<PromptImageAttachmentsProps> = (p
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={attachment.blob.url}
|
src={attachment.dataUrl}
|
||||||
alt={attachment.filename}
|
alt={attachment.filename}
|
||||||
class={props.newLayoutDesigns ? imageClassV2 : imageClass}
|
class={props.newLayoutDesigns ? imageClassV2 : imageClass}
|
||||||
onClick={() => props.onOpen(attachment)}
|
onClick={() => props.onOpen(attachment)}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import { ScopedKey } from "@/utils/server-scope"
|
||||||
import { createPromptSubmissionState } from "./submission-state"
|
import { createPromptSubmissionState } from "./submission-state"
|
||||||
import { normalizeSessionInfo } from "@/utils/session"
|
import { normalizeSessionInfo } from "@/utils/session"
|
||||||
import { Event } from "@opencode-ai/schema/event"
|
import { Event } from "@opencode-ai/schema/event"
|
||||||
import { blobDataUrl } from "@/utils/draft-store"
|
|
||||||
|
|
||||||
type PendingPrompt = {
|
type PendingPrompt = {
|
||||||
abort: AbortController
|
abort: AbortController
|
||||||
|
|
@ -96,12 +95,10 @@ export async function sendFollowupDraft(input: FollowupSendInput) {
|
||||||
providerID: input.draft.model.providerID,
|
providerID: input.draft.model.providerID,
|
||||||
variant: input.draft.variant,
|
variant: input.draft.variant,
|
||||||
},
|
},
|
||||||
files: await Promise.all(
|
files: images.map((attachment) => ({
|
||||||
images.map(async (attachment) => ({
|
uri: attachment.dataUrl,
|
||||||
uri: await blobDataUrl(attachment.blob, attachment.mime),
|
name: attachment.filename,
|
||||||
name: attachment.filename,
|
})),
|
||||||
})),
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -111,16 +108,10 @@ export async function sendFollowupDraft(input: FollowupSendInput) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const messageID = input.messageID ?? Identifier.ascending("message")
|
const messageID = input.messageID ?? Identifier.ascending("message")
|
||||||
const encodedImages = await Promise.all(
|
|
||||||
images.map(async (attachment) => ({
|
|
||||||
...attachment,
|
|
||||||
dataUrl: await blobDataUrl(attachment.blob, attachment.mime),
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
const { requestParts, optimisticParts } = buildRequestParts({
|
const { requestParts, optimisticParts } = buildRequestParts({
|
||||||
prompt: input.draft.prompt,
|
prompt: input.draft.prompt,
|
||||||
context: input.draft.context,
|
context: input.draft.context,
|
||||||
images: encodedImages,
|
images,
|
||||||
text,
|
text,
|
||||||
sessionID: input.draft.sessionID,
|
sessionID: input.draft.sessionID,
|
||||||
messageID,
|
messageID,
|
||||||
|
|
@ -525,12 +516,10 @@ export function createPromptSubmit(input: PromptSubmitInput) {
|
||||||
arguments: args.join(" "),
|
arguments: args.join(" "),
|
||||||
agent,
|
agent,
|
||||||
model: { id: model.modelID, providerID: model.providerID, variant },
|
model: { id: model.modelID, providerID: model.providerID, variant },
|
||||||
files: await Promise.all(
|
files: images.map((attachment) => ({
|
||||||
images.map(async (attachment) => ({
|
uri: attachment.dataUrl,
|
||||||
uri: await blobDataUrl(attachment.blob, attachment.mime),
|
name: attachment.filename,
|
||||||
name: attachment.filename,
|
})),
|
||||||
})),
|
|
||||||
),
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
serverSync().session.set("session_status", session.id, { type: "idle" })
|
serverSync().session.set("session_status", session.id, { type: "idle" })
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import {
|
||||||
bootstrapDirectory,
|
bootstrapDirectory,
|
||||||
loadAgentsQuery,
|
loadAgentsQuery,
|
||||||
loadCommands,
|
loadCommands,
|
||||||
loadGlobalConfigQuery,
|
|
||||||
loadPathQuery,
|
loadPathQuery,
|
||||||
loadProjectsQuery,
|
loadProjectsQuery,
|
||||||
loadProvidersQuery,
|
loadProvidersQuery,
|
||||||
|
|
@ -77,7 +76,6 @@ function directoryState() {
|
||||||
|
|
||||||
describe("bootstrapDirectory", () => {
|
describe("bootstrapDirectory", () => {
|
||||||
test("uses legacy MCP endpoints while refreshing a v1 directory", async () => {
|
test("uses legacy MCP endpoints while refreshing a v1 directory", async () => {
|
||||||
const legacyConfigReads: string[] = []
|
|
||||||
const mcpReads: string[] = []
|
const mcpReads: string[] = []
|
||||||
const [store, setStore] = directoryState()
|
const [store, setStore] = directoryState()
|
||||||
|
|
||||||
|
|
@ -93,12 +91,7 @@ describe("bootstrapDirectory", () => {
|
||||||
},
|
},
|
||||||
sdk: {
|
sdk: {
|
||||||
app: { agents: async () => ({ data: [{ name: "build", mode: "primary" }] }) },
|
app: { agents: async () => ({ data: [{ name: "build", mode: "primary" }] }) },
|
||||||
config: {
|
config: { get: async () => ({ data: {} }) },
|
||||||
get: async () => {
|
|
||||||
legacyConfigReads.push("directory")
|
|
||||||
return { data: {} }
|
|
||||||
},
|
|
||||||
},
|
|
||||||
session: { status: async () => ({ data: {} }) },
|
session: { status: async () => ({ data: {} }) },
|
||||||
vcs: { get: async () => ({ data: undefined }) },
|
vcs: { get: async () => ({ data: undefined }) },
|
||||||
command: {
|
command: {
|
||||||
|
|
@ -141,88 +134,8 @@ describe("bootstrapDirectory", () => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 80))
|
await new Promise((resolve) => setTimeout(resolve, 80))
|
||||||
|
|
||||||
expect(store.status).toBe("complete")
|
expect(store.status).toBe("complete")
|
||||||
expect(legacyConfigReads).toEqual(["directory"])
|
|
||||||
expect(mcpReads.sort()).toEqual(["command", "resource", "status"])
|
expect(mcpReads.sort()).toEqual(["command", "resource", "status"])
|
||||||
})
|
})
|
||||||
|
|
||||||
test("skips legacy config while refreshing a v2 directory", async () => {
|
|
||||||
const [store, setStore] = directoryState()
|
|
||||||
|
|
||||||
await bootstrapDirectory({
|
|
||||||
directory: "/project",
|
|
||||||
scope: ServerScope.local,
|
|
||||||
mcp: false,
|
|
||||||
global: {
|
|
||||||
config: {} satisfies Config,
|
|
||||||
path: { state: "", config: "", worktree: "/project", directory: "/project", home: "/home" },
|
|
||||||
project: [{ id: "project", worktree: "/project" } as Project],
|
|
||||||
provider,
|
|
||||||
},
|
|
||||||
sdk: {
|
|
||||||
config: {
|
|
||||||
get: async () => {
|
|
||||||
throw new Error("legacy directory config should not be called")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as unknown as OpencodeClient,
|
|
||||||
api,
|
|
||||||
store,
|
|
||||||
setStore,
|
|
||||||
vcsCache: { setStore() {} } as unknown as VcsCache,
|
|
||||||
loadSessions() {},
|
|
||||||
translate: (key) => key,
|
|
||||||
queryClient: new QueryClient(),
|
|
||||||
protocol: Promise.resolve("v2"),
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(store.status).toBe("partial")
|
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 80))
|
|
||||||
|
|
||||||
expect(store.status).toBe("complete")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("config queries", () => {
|
|
||||||
test("skips legacy global config for v2 servers", async () => {
|
|
||||||
const sdk = {
|
|
||||||
global: {
|
|
||||||
config: {
|
|
||||||
get: async () => {
|
|
||||||
throw new Error("legacy global config should not be called")
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as unknown as OpencodeClient
|
|
||||||
|
|
||||||
const result = await new QueryClient().fetchQuery(
|
|
||||||
loadGlobalConfigQuery(ServerScope.local, sdk, Promise.resolve("v2")),
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(result).toEqual({})
|
|
||||||
})
|
|
||||||
|
|
||||||
test("loads legacy global config for v1 servers", async () => {
|
|
||||||
const calls: string[] = []
|
|
||||||
const config = { shell: "zsh" } satisfies Config
|
|
||||||
const sdk = {
|
|
||||||
global: {
|
|
||||||
config: {
|
|
||||||
get: async () => {
|
|
||||||
calls.push("global")
|
|
||||||
return { data: config }
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as unknown as OpencodeClient
|
|
||||||
|
|
||||||
const result = await new QueryClient().fetchQuery(
|
|
||||||
loadGlobalConfigQuery(ServerScope.local, sdk, Promise.resolve("v1")),
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(result).toEqual(config)
|
|
||||||
expect(calls).toEqual(["global"])
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("query keys", () => {
|
describe("query keys", () => {
|
||||||
|
|
|
||||||
|
|
@ -105,13 +105,10 @@ function showErrors(input: {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadGlobalConfigQuery = (scope: ServerScope, sdk: OpencodeClient, protocol?: Promise<ServerProtocol>) =>
|
export const loadGlobalConfigQuery = (scope: ServerScope, sdk: OpencodeClient) =>
|
||||||
queryOptions({
|
queryOptions({
|
||||||
queryKey: [scope, "config"],
|
queryKey: [scope, "config"],
|
||||||
queryFn: async () => {
|
queryFn: () => retry(() => sdk.global.config.get().then((x) => x.data!)),
|
||||||
if ((await protocol) !== "v1") return {}
|
|
||||||
return retry(() => sdk.global.config.get().then((x) => x.data!))
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
type ProjectApi = {
|
type ProjectApi = {
|
||||||
|
|
@ -152,7 +149,7 @@ export async function bootstrapGlobal(input: {
|
||||||
queryClient: QueryClient
|
queryClient: QueryClient
|
||||||
}) {
|
}) {
|
||||||
const slow = [
|
const slow = [
|
||||||
() => input.queryClient.fetchQuery(loadGlobalConfigQuery(input.scope, input.serverSDK, input.protocol)),
|
() => input.queryClient.fetchQuery(loadGlobalConfigQuery(input.scope, input.serverSDK)),
|
||||||
() =>
|
() =>
|
||||||
input.queryClient.fetchQuery(
|
input.queryClient.fetchQuery(
|
||||||
loadProvidersQuery(input.scope, null, input.serverAPI, input.serverSDK, input.protocol),
|
loadProvidersQuery(input.scope, null, input.serverAPI, input.serverSDK, input.protocol),
|
||||||
|
|
@ -379,10 +376,7 @@ export async function bootstrapDirectory(input: {
|
||||||
.ensureQueryData(loadAgentsQuery(input.scope, input.directory, input.api.agent, input.sdk, input.protocol))
|
.ensureQueryData(loadAgentsQuery(input.scope, input.directory, input.api.agent, input.sdk, input.protocol))
|
||||||
.then((data) => input.setStore("agent", data)),
|
.then((data) => input.setStore("agent", data)),
|
||||||
() =>
|
() =>
|
||||||
retry(async () => {
|
retry(() => input.sdk.config.get().then((x) => input.setStore("config", reconcile(x.data!, { merge: false })))),
|
||||||
if ((await input.protocol) !== "v1") return
|
|
||||||
return input.sdk.config.get().then((x) => input.setStore("config", reconcile(x.data!, { merge: false })))
|
|
||||||
}),
|
|
||||||
() =>
|
() =>
|
||||||
retry(() =>
|
retry(() =>
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import type { DesktopMenuAction } from "../desktop-menu"
|
||||||
import { ServerConnection } from "./server"
|
import { ServerConnection } from "./server"
|
||||||
import type { WslServersPlatform } from "../wsl/types"
|
import type { WslServersPlatform } from "../wsl/types"
|
||||||
import type { UpdaterPlatform } from "../updater"
|
import type { UpdaterPlatform } from "../updater"
|
||||||
import type { DraftStore } from "@/utils/draft-store"
|
|
||||||
|
|
||||||
type PickerPaths = string | string[] | null
|
type PickerPaths = string | string[] | null
|
||||||
type OpenDirectoryPickerOptions = { title?: string; multiple?: boolean }
|
type OpenDirectoryPickerOptions = { title?: string; multiple?: boolean }
|
||||||
|
|
@ -65,9 +64,6 @@ type PlatformBase = {
|
||||||
/** Storage mechanism, defaults to localStorage */
|
/** Storage mechanism, defaults to localStorage */
|
||||||
storage?: (name?: string) => SyncStorage | AsyncStorage
|
storage?: (name?: string) => SyncStorage | AsyncStorage
|
||||||
|
|
||||||
/** Prompt drafts, history, and their blobs. */
|
|
||||||
draftStore?: DraftStore
|
|
||||||
|
|
||||||
/** Stable platform window identity for window-scoped persistence */
|
/** Stable platform window identity for window-scoped persistence */
|
||||||
windowID?: string
|
windowID?: string
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import { createStore, type SetStoreFunction } from "solid-js/store"
|
||||||
import type { FileSelection } from "@/context/file"
|
import type { FileSelection } from "@/context/file"
|
||||||
import { Persist, persisted } from "@/utils/persist"
|
import { Persist, persisted } from "@/utils/persist"
|
||||||
import type { ServerScope } from "@/utils/server-scope"
|
import type { ServerScope } from "@/utils/server-scope"
|
||||||
import type { BlobReference } from "@/utils/draft-store"
|
|
||||||
|
|
||||||
interface PartBase {
|
interface PartBase {
|
||||||
content: string
|
content: string
|
||||||
|
|
@ -38,7 +37,7 @@ export interface ImageAttachmentPart {
|
||||||
filename: string
|
filename: string
|
||||||
sourcePath?: string
|
sourcePath?: string
|
||||||
mime: string
|
mime: string
|
||||||
blob: BlobReference
|
dataUrl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ContentPart = TextPart | FileAttachmentPart | AgentPart | ImageAttachmentPart
|
export type ContentPart = TextPart | FileAttachmentPart | AgentPart | ImageAttachmentPart
|
||||||
|
|
@ -169,9 +168,9 @@ function createPromptActions(setStore: SetStoreFunction<PromptStore>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function promptTarget(serverScope: ServerScope, scope: PromptScope) {
|
function promptTarget(serverScope: ServerScope, scope: PromptScope) {
|
||||||
if ("draftID" in scope) return Persist.prompt(Persist.draft(scope.draftID, "prompt"))
|
if ("draftID" in scope) return Persist.draft(scope.draftID, "prompt")
|
||||||
const legacy = `${scope.dir}/prompt${scope.id ? "/" + scope.id : ""}.v2`
|
const legacy = `${scope.dir}/prompt${scope.id ? "/" + scope.id : ""}.v2`
|
||||||
return Persist.prompt(Persist.serverScoped(serverScope, scope.dir, scope.id, "prompt", [legacy]))
|
return Persist.serverScoped(serverScope, scope.dir, scope.id, "prompt", [legacy])
|
||||||
}
|
}
|
||||||
|
|
||||||
function promptStore(initial?: InitialPrompt): PromptStore {
|
function promptStore(initial?: InitialPrompt): PromptStore {
|
||||||
|
|
@ -246,7 +245,7 @@ export function createPromptSession(serverScope: ServerScope, scope: PromptScope
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createDraftPromptSession(draftID: string, initial?: InitialPrompt) {
|
export function createDraftPromptSession(draftID: string, initial?: InitialPrompt) {
|
||||||
return createPersistedPrompt(Persist.prompt(Persist.draft(draftID, "prompt")), initial)
|
return createPersistedPrompt(Persist.draft(draftID, "prompt"), initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PromptSession = ReturnType<typeof createPromptSession>
|
export type PromptSession = ReturnType<typeof createPromptSession>
|
||||||
|
|
|
||||||
|
|
@ -184,7 +184,7 @@ function makeQueryOptionsApi(
|
||||||
protocol: Promise<"v1" | "v2">,
|
protocol: Promise<"v1" | "v2">,
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
globalConfig: () => loadGlobalConfigQuery(scope, serverSDK(), protocol),
|
globalConfig: () => loadGlobalConfigQuery(scope, serverSDK()),
|
||||||
projects: () => loadProjectsQuery(scope, serverAPI.project),
|
projects: () => loadProjectsQuery(scope, serverAPI.project),
|
||||||
providers: (directory: PathKey | null) =>
|
providers: (directory: PathKey | null) =>
|
||||||
loadProvidersQuery(scope, directory, serverAPI, directory ? sdkFor(directory) : serverSDK(), protocol),
|
loadProvidersQuery(scope, directory, serverAPI, directory ? sdkFor(directory) : serverSDK(), protocol),
|
||||||
|
|
|
||||||
|
|
@ -101,10 +101,7 @@ export const { use: useTabs, provider: TabsProvider } = createSimpleContext({
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeDraftPersisted = (draftID: string) => {
|
const removeDraftPersisted = (draftID: string) => {
|
||||||
for (const key of draftPersistedKeys()) {
|
for (const key of draftPersistedKeys()) removePersisted(Persist.draft(draftID, key), platform)
|
||||||
const target = Persist.draft(draftID, key)
|
|
||||||
removePersisted(key === "prompt" ? Persist.prompt(target) : target, platform)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeInfo = (key: string) => {
|
const removeInfo = (key: string) => {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import * as Sentry from "@sentry/solid"
|
||||||
import { render } from "solid-js/web"
|
import { render } from "solid-js/web"
|
||||||
import { AppBaseProviders, AppInterface } from "@/app"
|
import { AppBaseProviders, AppInterface } from "@/app"
|
||||||
import { type Platform, PlatformProvider } from "@/context/platform"
|
import { type Platform, PlatformProvider } from "@/context/platform"
|
||||||
import { createBrowserDraftStore } from "@/utils/draft-store"
|
|
||||||
import { dict as en } from "@/i18n/en"
|
import { dict as en } from "@/i18n/en"
|
||||||
import { dict as zh } from "@/i18n/zh"
|
import { dict as zh } from "@/i18n/zh"
|
||||||
import { authFromToken } from "@/utils/server"
|
import { authFromToken } from "@/utils/server"
|
||||||
|
|
@ -117,7 +116,6 @@ const clearAuthToken = () => {
|
||||||
|
|
||||||
const platform: Platform = {
|
const platform: Platform = {
|
||||||
platform: "web",
|
platform: "web",
|
||||||
draftStore: createBrowserDraftStore(),
|
|
||||||
version: pkg.version,
|
version: pkg.version,
|
||||||
openExternal,
|
openExternal,
|
||||||
restart,
|
restart,
|
||||||
|
|
|
||||||
|
|
@ -27,4 +27,3 @@ export {
|
||||||
type WslServersState,
|
type WslServersState,
|
||||||
} from "./wsl/types"
|
} from "./wsl/types"
|
||||||
export { ServerConnection } from "./context/server"
|
export { ServerConnection } from "./context/server"
|
||||||
export { createDraftStore, type DraftStore } from "./utils/draft-store"
|
|
||||||
|
|
|
||||||
|
|
@ -167,41 +167,4 @@ describe("current session timeline rows", () => {
|
||||||
"thinking:msg_2",
|
"thinking:msg_2",
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
test("removes a failed assistant error when the turn continues streaming", () => {
|
|
||||||
const source = [
|
|
||||||
{ id: "msg_user", type: "user", text: "recover", time: { created: 1 } },
|
|
||||||
{
|
|
||||||
id: "msg_failed",
|
|
||||||
type: "assistant",
|
|
||||||
agent: "build",
|
|
||||||
model: { id: "model", providerID: "provider" },
|
|
||||||
content: [],
|
|
||||||
error: { type: "ProviderError", message: "temporary failure" },
|
|
||||||
time: { created: 2, completed: 3 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "msg_recovery",
|
|
||||||
type: "assistant",
|
|
||||||
agent: "build",
|
|
||||||
model: { id: "model", providerID: "provider" },
|
|
||||||
content: [{ type: "text", text: "streaming again" }],
|
|
||||||
time: { created: 4 },
|
|
||||||
},
|
|
||||||
] satisfies SessionMessageInfo[]
|
|
||||||
const normalized = normalizeSessionMessages("ses_1", source)
|
|
||||||
const messages = new Map(normalized.messages.map((message) => [message.id, message]))
|
|
||||||
|
|
||||||
const result = Timeline.constructSessionMessageRows(
|
|
||||||
source,
|
|
||||||
(messageID) => messages.get(messageID),
|
|
||||||
(messageID) => normalized.parts.get(messageID) ?? [],
|
|
||||||
true,
|
|
||||||
"busy",
|
|
||||||
true,
|
|
||||||
normalized.messages.filter((message) => message.role === "user"),
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(result.rows.map((row) => row._tag)).toEqual(["UserMessage", "AssistantPart"])
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -116,8 +116,7 @@ export namespace Timeline {
|
||||||
const compaction = userParts.some((p) => p.type === "compaction")
|
const compaction = userParts.some((p) => p.type === "compaction")
|
||||||
const interruptedMessageIndex = assistantMessages.findIndex((m) => m.error?.name === "MessageAbortedError")
|
const interruptedMessageIndex = assistantMessages.findIndex((m) => m.error?.name === "MessageAbortedError")
|
||||||
const interrupted = interruptedMessageIndex !== -1
|
const interrupted = interruptedMessageIndex !== -1
|
||||||
const latestError = assistantMessages.at(-1)?.error
|
const error = assistantMessages.find((m) => m.error && m.error.name !== "MessageAbortedError")?.error
|
||||||
const error = latestError?.name === "MessageAbortedError" ? undefined : latestError
|
|
||||||
|
|
||||||
const assistantPartRefs = assistantMessages.flatMap((message, messageIndex) =>
|
const assistantPartRefs = assistantMessages.flatMap((message, messageIndex) =>
|
||||||
getMessageParts(message.id)
|
getMessageParts(message.id)
|
||||||
|
|
|
||||||
|
|
@ -1,171 +0,0 @@
|
||||||
import type { AsyncStorage } from "@solid-primitives/storage"
|
|
||||||
|
|
||||||
export type BlobReference = { id: string; url: string }
|
|
||||||
|
|
||||||
type Driver = {
|
|
||||||
get(key: string): Promise<string | null>
|
|
||||||
set(key: string, value: string): Promise<void>
|
|
||||||
remove(key: string): Promise<void>
|
|
||||||
putBlob(blob: Blob): Promise<string>
|
|
||||||
getBlob(id: string): Promise<Blob | null>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DraftStore = AsyncStorage & { putBlob(blob: Blob): Promise<BlobReference> }
|
|
||||||
const urls = new Map<string, string>()
|
|
||||||
|
|
||||||
function blobUrl(id: string, blob: Blob) {
|
|
||||||
const existing = urls.get(id)
|
|
||||||
if (existing) return existing
|
|
||||||
const url = URL.createObjectURL(blob)
|
|
||||||
urls.set(id, url)
|
|
||||||
return url
|
|
||||||
}
|
|
||||||
|
|
||||||
async function blobID(blob: Blob) {
|
|
||||||
const id = Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", await blob.arrayBuffer())))
|
|
||||||
.map((byte) => byte.toString(16).padStart(2, "0"))
|
|
||||||
.join("")
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createBlobReference(blob: Blob): Promise<BlobReference> {
|
|
||||||
const id = await blobID(blob)
|
|
||||||
return { id, url: blobUrl(id, blob) }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createDraftStore(driver: Driver): DraftStore {
|
|
||||||
const versions = new Map<string, number>()
|
|
||||||
const putBlob = async (blob: Blob) => {
|
|
||||||
const id = await driver.putBlob(blob)
|
|
||||||
return { id, url: blobUrl(id, blob) }
|
|
||||||
}
|
|
||||||
const encode = async (value: unknown): Promise<unknown> => {
|
|
||||||
if (Array.isArray(value)) return Promise.all(value.map(encode))
|
|
||||||
if (!value || typeof value !== "object") return value
|
|
||||||
const item = value as Record<string, unknown>
|
|
||||||
if (item.type === "image" && typeof item.dataUrl === "string") {
|
|
||||||
const blob = await fetch(item.dataUrl).then((response) => response.blob())
|
|
||||||
const { dataUrl: _, ...rest } = item
|
|
||||||
return { ...rest, blob: { id: await driver.putBlob(blob) } }
|
|
||||||
}
|
|
||||||
if ("blob" in item && item.blob && typeof item.blob === "object") {
|
|
||||||
const blob = item.blob as Record<string, unknown>
|
|
||||||
if (typeof blob.id === "string" && blob.id.startsWith("data:")) {
|
|
||||||
const data = await fetch(blob.id).then((response) => response.blob())
|
|
||||||
return { ...item, blob: { id: await driver.putBlob(data) } }
|
|
||||||
}
|
|
||||||
return { ...item, blob: { id: blob.id } }
|
|
||||||
}
|
|
||||||
return Object.fromEntries(
|
|
||||||
await Promise.all(Object.entries(item).map(async ([key, entry]) => [key, await encode(entry)])),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
const decode = async (value: unknown): Promise<unknown> => {
|
|
||||||
if (Array.isArray(value)) return Promise.all(value.map(decode))
|
|
||||||
if (!value || typeof value !== "object") return value
|
|
||||||
const item = value as Record<string, unknown>
|
|
||||||
if (item.blob && typeof item.blob === "object") {
|
|
||||||
const ref = item.blob as Record<string, unknown>
|
|
||||||
if (typeof ref.id === "string") {
|
|
||||||
const blob = await driver.getBlob(ref.id)
|
|
||||||
if (blob) return { ...item, blob: { id: ref.id, url: blobUrl(ref.id, blob) } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Object.fromEntries(
|
|
||||||
await Promise.all(Object.entries(item).map(async ([key, entry]) => [key, await decode(entry)])),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
getItem: async (key) => {
|
|
||||||
const value = await driver.get(key)
|
|
||||||
return value === null ? null : JSON.stringify(await decode(JSON.parse(value)))
|
|
||||||
},
|
|
||||||
setItem: async (key, value) => {
|
|
||||||
const version = (versions.get(key) ?? 0) + 1
|
|
||||||
versions.set(key, version)
|
|
||||||
const encoded = JSON.stringify(await encode(JSON.parse(value)))
|
|
||||||
if (versions.get(key) === version) await driver.set(key, encoded)
|
|
||||||
},
|
|
||||||
removeItem: async (key) => {
|
|
||||||
versions.set(key, (versions.get(key) ?? 0) + 1)
|
|
||||||
await driver.remove(key)
|
|
||||||
},
|
|
||||||
putBlob,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createBrowserDraftStore(): DraftStore {
|
|
||||||
const request = indexedDB.open("opencode-drafts", 1)
|
|
||||||
request.addEventListener("upgradeneeded", () => {
|
|
||||||
request.result.createObjectStore("documents")
|
|
||||||
request.result.createObjectStore("blobs")
|
|
||||||
})
|
|
||||||
const db = new Promise<IDBDatabase>((resolve, reject) => {
|
|
||||||
request.addEventListener("success", () => {
|
|
||||||
const database = request.result
|
|
||||||
const transaction = database.transaction(["documents", "blobs"], "readwrite")
|
|
||||||
const documents = transaction.objectStore("documents").getAll()
|
|
||||||
documents.addEventListener("success", () => {
|
|
||||||
const used = new Set<string>()
|
|
||||||
JSON.parse(`[${documents.result.join(",")}]`, (_key, item) => {
|
|
||||||
if (item?.blob && typeof item.blob.id === "string") used.add(item.blob.id)
|
|
||||||
return item
|
|
||||||
})
|
|
||||||
const blobs = transaction.objectStore("blobs").openKeyCursor()
|
|
||||||
blobs.addEventListener("success", () => {
|
|
||||||
const cursor = blobs.result
|
|
||||||
if (!cursor) return
|
|
||||||
if (!used.has(String(cursor.key))) cursor.delete()
|
|
||||||
cursor.continue()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
transaction.addEventListener("complete", () => resolve(database))
|
|
||||||
transaction.addEventListener("abort", () => resolve(database))
|
|
||||||
})
|
|
||||||
request.addEventListener("error", () => reject(request.error))
|
|
||||||
})
|
|
||||||
const get = async (store: string, key: string) => {
|
|
||||||
const result = (await db).transaction(store).objectStore(store).get(key)
|
|
||||||
return new Promise<unknown>((resolve, reject) => {
|
|
||||||
result.addEventListener("success", () => resolve(result.result))
|
|
||||||
result.addEventListener("error", () => reject(result.error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const write = async (store: string, key: string, value?: unknown) => {
|
|
||||||
const transaction = (await db).transaction(store, "readwrite")
|
|
||||||
if (value === undefined) transaction.objectStore(store).delete(key)
|
|
||||||
else transaction.objectStore(store).put(value, key)
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
|
||||||
transaction.addEventListener("complete", () => resolve())
|
|
||||||
transaction.addEventListener("error", () => reject(transaction.error))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return createDraftStore({
|
|
||||||
get: async (key) => ((await get("documents", key)) as string | undefined) ?? null,
|
|
||||||
set: (key, value) => write("documents", key, value),
|
|
||||||
remove: (key) => write("documents", key),
|
|
||||||
putBlob: async (blob) => {
|
|
||||||
const id = await blobID(blob)
|
|
||||||
await write("blobs", id, blob)
|
|
||||||
return id
|
|
||||||
},
|
|
||||||
getBlob: async (id) => ((await get("blobs", id)) as Blob | undefined) ?? null,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function blobDataUrl(blob: BlobReference, mime: string) {
|
|
||||||
const data = await fetch(blob.url).then((response) => response.blob())
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
|
||||||
const reader = new FileReader()
|
|
||||||
reader.addEventListener("error", () => reject(reader.error))
|
|
||||||
reader.addEventListener("load", () => {
|
|
||||||
const value = typeof reader.result === "string" ? reader.result : ""
|
|
||||||
resolve(`data:${mime};base64,${value.slice(value.indexOf(",") + 1)}`)
|
|
||||||
})
|
|
||||||
reader.readAsDataURL(data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createLegacyBlobReference(dataUrl: string): BlobReference {
|
|
||||||
return { id: dataUrl, url: dataUrl }
|
|
||||||
}
|
|
||||||
|
|
@ -15,7 +15,6 @@ type PersistedWithReady<T> = [
|
||||||
]
|
]
|
||||||
|
|
||||||
type PersistTarget = {
|
type PersistTarget = {
|
||||||
draft?: boolean
|
|
||||||
storage?: string
|
storage?: string
|
||||||
scope?: "window"
|
scope?: "window"
|
||||||
legacyStorageNames?: string[]
|
legacyStorageNames?: string[]
|
||||||
|
|
@ -296,14 +295,6 @@ async function removeAsync(storage: AsyncStorage, key: string) {
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toAsyncStorage(storage: SyncStorage | AsyncStorage): AsyncStorage {
|
|
||||||
return {
|
|
||||||
getItem: async (key) => storage.getItem(key),
|
|
||||||
setItem: async (key, value) => storage.setItem(key, value),
|
|
||||||
removeItem: async (key) => storage.removeItem(key),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function migrateLegacyAsync(input: {
|
async function migrateLegacyAsync(input: {
|
||||||
current: AsyncStorage
|
current: AsyncStorage
|
||||||
legacyStore?: AsyncStorage
|
legacyStore?: AsyncStorage
|
||||||
|
|
@ -522,9 +513,6 @@ export const Persist = {
|
||||||
if (session) return Persist.serverSession(scope, dir, session, key, legacy)
|
if (session) return Persist.serverSession(scope, dir, session, key, legacy)
|
||||||
return Persist.serverWorkspace(scope, dir, key, legacy)
|
return Persist.serverWorkspace(scope, dir, key, legacy)
|
||||||
},
|
},
|
||||||
prompt(target: PersistTarget): PersistTarget {
|
|
||||||
return { ...target, draft: true }
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveTarget(target: PersistTarget, platform: Platform): PersistTarget {
|
function resolveTarget(target: PersistTarget, platform: Platform): PersistTarget {
|
||||||
|
|
@ -538,12 +526,9 @@ function resolveTarget(target: PersistTarget, platform: Platform): PersistTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removePersisted(
|
export function removePersisted(
|
||||||
target: { draft?: boolean; storage?: string; legacyStorageNames?: string[]; key: string },
|
target: { storage?: string; legacyStorageNames?: string[]; key: string },
|
||||||
platform?: Platform,
|
platform?: Platform,
|
||||||
) {
|
) {
|
||||||
if (target.draft && platform?.draftStore) {
|
|
||||||
void platform.draftStore.removeItem(`${target.storage ?? "default"}:${target.key}`)
|
|
||||||
}
|
|
||||||
const isDesktop = platform?.platform === "desktop" && !!platform.storage
|
const isDesktop = platform?.platform === "desktop" && !!platform.storage
|
||||||
|
|
||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
|
|
@ -576,17 +561,8 @@ export function persisted<T>(
|
||||||
const legacy = config.legacy ?? []
|
const legacy = config.legacy ?? []
|
||||||
|
|
||||||
const isDesktop = platform.platform === "desktop" && !!platform.storage
|
const isDesktop = platform.platform === "desktop" && !!platform.storage
|
||||||
const draft = config.draft ? platform.draftStore : undefined
|
|
||||||
|
|
||||||
const currentStorage = (() => {
|
const currentStorage = (() => {
|
||||||
if (draft) {
|
|
||||||
const prefix = `${config.storage ?? "default"}:`
|
|
||||||
return {
|
|
||||||
getItem: (key: string) => draft.getItem(prefix + key),
|
|
||||||
setItem: (key: string, value: string) => draft.setItem(prefix + key, value),
|
|
||||||
removeItem: (key: string) => draft.removeItem(prefix + key),
|
|
||||||
} satisfies AsyncStorage
|
|
||||||
}
|
|
||||||
if (isDesktop) return platform.storage?.(config.storage)
|
if (isDesktop) return platform.storage?.(config.storage)
|
||||||
if (!config.storage) return localStorageDirect()
|
if (!config.storage) return localStorageDirect()
|
||||||
return localStorageWithPrefix(config.storage)
|
return localStorageWithPrefix(config.storage)
|
||||||
|
|
@ -601,7 +577,7 @@ export function persisted<T>(
|
||||||
const legacyStorageNames = config.legacyStorageNames ?? []
|
const legacyStorageNames = config.legacyStorageNames ?? []
|
||||||
|
|
||||||
const storage = (() => {
|
const storage = (() => {
|
||||||
if (!isDesktop && !draft) {
|
if (!isDesktop) {
|
||||||
const current = currentStorage as SyncStorage
|
const current = currentStorage as SyncStorage
|
||||||
const legacyStore = legacyStorage as SyncStorage
|
const legacyStore = legacyStorage as SyncStorage
|
||||||
const legacyStores = legacyStorageNames.map(localStorageWithPrefix)
|
const legacyStores = legacyStorageNames.map(localStorageWithPrefix)
|
||||||
|
|
@ -633,26 +609,15 @@ export function persisted<T>(
|
||||||
|
|
||||||
const current = currentStorage as AsyncStorage
|
const current = currentStorage as AsyncStorage
|
||||||
const legacyStore = legacyStorage as AsyncStorage | undefined
|
const legacyStore = legacyStorage as AsyncStorage | undefined
|
||||||
const oldCurrent = draft
|
const legacyStores = legacyStorageNames
|
||||||
? isDesktop
|
.map((name) => platform.storage?.(name) as AsyncStorage | undefined)
|
||||||
? platform.storage?.(config.storage)
|
|
||||||
: config.storage
|
|
||||||
? localStorageWithPrefix(config.storage)
|
|
||||||
: localStorageDirect()
|
|
||||||
: undefined
|
|
||||||
const legacyStores = [
|
|
||||||
oldCurrent,
|
|
||||||
...legacyStorageNames.map((name) => (isDesktop ? platform.storage?.(name) : localStorageWithPrefix(name))),
|
|
||||||
]
|
|
||||||
.filter((x) => !!x)
|
.filter((x) => !!x)
|
||||||
.map(toAsyncStorage)
|
|
||||||
let draftLatest: string | undefined
|
|
||||||
|
|
||||||
const api: AsyncStorage = {
|
const api: AsyncStorage = {
|
||||||
getItem: async (key) => {
|
getItem: async (key) => {
|
||||||
const value = await readCurrentAsync({ storage: current, key, defaults, migrate: config.migrate })
|
const value = await readCurrentAsync({ storage: current, key, defaults, migrate: config.migrate })
|
||||||
if (value !== undefined) return value
|
if (value !== undefined) return value
|
||||||
const migrated = await migrateLegacyAsync({
|
return migrateLegacyAsync({
|
||||||
current,
|
current,
|
||||||
legacyStore,
|
legacyStore,
|
||||||
stores: legacyStores,
|
stores: legacyStores,
|
||||||
|
|
@ -661,15 +626,8 @@ export function persisted<T>(
|
||||||
defaults,
|
defaults,
|
||||||
migrate: config.migrate,
|
migrate: config.migrate,
|
||||||
})
|
})
|
||||||
if (draftLatest === undefined) {
|
|
||||||
if (draft && migrated !== null) return (await current.getItem(key)) ?? migrated
|
|
||||||
return migrated
|
|
||||||
}
|
|
||||||
await current.setItem(key, draftLatest)
|
|
||||||
return draftLatest
|
|
||||||
},
|
},
|
||||||
setItem: async (key, value) => {
|
setItem: async (key, value) => {
|
||||||
if (draft) draftLatest = value
|
|
||||||
await current.setItem(key, value)
|
await current.setItem(key, value)
|
||||||
},
|
},
|
||||||
removeItem: async (key) => {
|
removeItem: async (key) => {
|
||||||
|
|
|
||||||
|
|
@ -37,18 +37,8 @@ describe("extractPromptFromParts", () => {
|
||||||
expect(result).toHaveLength(3)
|
expect(result).toHaveLength(3)
|
||||||
expect(result[0]).toMatchObject({ type: "text", content: "check these" })
|
expect(result[0]).toMatchObject({ type: "text", content: "check these" })
|
||||||
expect(result.slice(1)).toMatchObject([
|
expect(result.slice(1)).toMatchObject([
|
||||||
{
|
{ type: "image", filename: "a.png", mime: "image/png", dataUrl: "data:image/png;base64,AAA" },
|
||||||
type: "image",
|
{ type: "image", filename: "b.pdf", mime: "application/pdf", dataUrl: "data:application/pdf;base64,BBB" },
|
||||||
filename: "a.png",
|
|
||||||
mime: "image/png",
|
|
||||||
blob: expect.objectContaining({ id: expect.any(String) }),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "image",
|
|
||||||
filename: "b.pdf",
|
|
||||||
mime: "application/pdf",
|
|
||||||
blob: expect.objectContaining({ id: expect.any(String) }),
|
|
||||||
},
|
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import type { AgentPart as MessageAgentPart, FilePart, Part, TextPart } from "@opencode-ai/sdk/v2"
|
import type { AgentPart as MessageAgentPart, FilePart, Part, TextPart } from "@opencode-ai/sdk/v2"
|
||||||
import type { AgentPart, FileAttachmentPart, ImageAttachmentPart, Prompt } from "@/context/prompt"
|
import type { AgentPart, FileAttachmentPart, ImageAttachmentPart, Prompt } from "@/context/prompt"
|
||||||
import { createLegacyBlobReference } from "@/utils/draft-store"
|
|
||||||
|
|
||||||
type Inline =
|
type Inline =
|
||||||
| {
|
| {
|
||||||
|
|
@ -108,7 +107,7 @@ export function extractPromptFromParts(parts: Part[], opts?: { directory?: strin
|
||||||
id: filePart.id,
|
id: filePart.id,
|
||||||
filename: filePart.filename ?? attachmentName,
|
filename: filePart.filename ?? attachmentName,
|
||||||
mime: filePart.mime,
|
mime: filePart.mime,
|
||||||
blob: createLegacyBlobReference(filePart.url),
|
dataUrl: filePart.url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { beforeAll, describe, expect, mock, test } from "bun:test"
|
||||||
import type { AsyncStorage } from "@solid-primitives/storage"
|
import type { AsyncStorage } from "@solid-primitives/storage"
|
||||||
import { createEffect, createRoot } from "solid-js"
|
import { createEffect, createRoot } from "solid-js"
|
||||||
import { ServerScope } from "@/utils/server-scope"
|
import { ServerScope } from "@/utils/server-scope"
|
||||||
import { createDraftStore } from "@/utils/draft-store"
|
|
||||||
|
|
||||||
let Prompt: typeof import("@/context/prompt")
|
let Prompt: typeof import("@/context/prompt")
|
||||||
let read: ((value: string | null) => void) | undefined
|
let read: ((value: string | null) => void) | undefined
|
||||||
|
|
@ -31,7 +30,7 @@ beforeAll(async () => {
|
||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
mock.module("@/context/platform", () => ({
|
mock.module("@/context/platform", () => ({
|
||||||
usePlatform: () => ({ platform: "desktop", storage: () => storage, draftStore: storage }),
|
usePlatform: () => ({ platform: "desktop", storage: () => storage }),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
Prompt = await import("@/context/prompt")
|
Prompt = await import("@/context/prompt")
|
||||||
|
|
@ -70,50 +69,3 @@ describe("prompt persistence", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test("moves legacy image data URLs into blobs and hydrates object URLs", async () => {
|
|
||||||
const documents = new Map<string, string>()
|
|
||||||
const blobs = new Map<string, Blob>()
|
|
||||||
const store = createDraftStore({
|
|
||||||
get: async (key) => documents.get(key) ?? null,
|
|
||||||
set: async (key, value) => void documents.set(key, value),
|
|
||||||
remove: async (key) => void documents.delete(key),
|
|
||||||
putBlob: async (blob) => {
|
|
||||||
const id = String(blob.size)
|
|
||||||
blobs.set(id, blob)
|
|
||||||
return id
|
|
||||||
},
|
|
||||||
getBlob: async (id) => blobs.get(id) ?? null,
|
|
||||||
})
|
|
||||||
|
|
||||||
await store.setItem("prompt", JSON.stringify({ prompt: [{ type: "image", dataUrl: "data:image/png;base64,YQ==" }] }))
|
|
||||||
expect(documents.get("prompt")).not.toContain("dataUrl")
|
|
||||||
const value = JSON.parse((await store.getItem("prompt"))!)
|
|
||||||
expect(value.prompt[0].blob.id).toBe("1")
|
|
||||||
expect(value.prompt[0].blob.url).toStartWith("blob:")
|
|
||||||
})
|
|
||||||
|
|
||||||
test("does not let delayed blob migration overwrite a newer draft", async () => {
|
|
||||||
const documents = new Map<string, string>()
|
|
||||||
const migration = Promise.withResolvers<void>()
|
|
||||||
const store = createDraftStore({
|
|
||||||
get: async () => null,
|
|
||||||
set: async (key, value) => void documents.set(key, value),
|
|
||||||
remove: async () => undefined,
|
|
||||||
putBlob: async () => {
|
|
||||||
await migration.promise
|
|
||||||
return "blob"
|
|
||||||
},
|
|
||||||
getBlob: async () => null,
|
|
||||||
})
|
|
||||||
const older = store.setItem(
|
|
||||||
"prompt",
|
|
||||||
JSON.stringify({ prompt: [{ type: "image", dataUrl: "data:image/png;base64,YQ==" }] }),
|
|
||||||
)
|
|
||||||
await Bun.sleep(0)
|
|
||||||
await store.setItem("prompt", JSON.stringify({ prompt: [{ type: "text", content: "latest" }] }))
|
|
||||||
migration.resolve()
|
|
||||||
await older
|
|
||||||
|
|
||||||
expect(documents.get("prompt")).toContain("latest")
|
|
||||||
})
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ const models = [
|
||||||
{ name: "Kimi K2.6", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
{ name: "Kimi K2.6", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||||
{ name: "MiMo-V2.5-Pro", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
{ name: "MiMo-V2.5-Pro", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||||
{ name: "MiMo-V2.5", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
{ name: "MiMo-V2.5", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||||
{ name: "Qwen3.8 Max", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
|
||||||
{ name: "Qwen3.7 Max", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
{ name: "Qwen3.7 Max", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||||
{ name: "Qwen3.7 Plus", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
{ name: "Qwen3.7 Plus", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||||
{ name: "Qwen3.6 Plus", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
{ name: "Qwen3.6 Plus", training: "go.faq.a5.notUsed", retention: "go.faq.a5.retention0" },
|
||||||
|
|
@ -68,7 +67,6 @@ function LimitsGraph(props: { href: string }) {
|
||||||
const graph = [
|
const graph = [
|
||||||
{ id: "grok-4.5", name: "Grok 4.5", req: 120, d: "50ms" },
|
{ id: "grok-4.5", name: "Grok 4.5", req: 120, d: "50ms" },
|
||||||
{ id: "kimi-k3", name: "Kimi K3", req: 110, d: "75ms" },
|
{ id: "kimi-k3", name: "Kimi K3", req: 110, d: "75ms" },
|
||||||
{ id: "qwen3.8-max", name: "Qwen3.8 Max", req: 160, d: "90ms" },
|
|
||||||
{ id: "glm-5.2", name: "GLM-5.2", req: 880, d: "100ms" },
|
{ id: "glm-5.2", name: "GLM-5.2", req: 880, d: "100ms" },
|
||||||
{ id: "minimax-m3", name: "MiniMax M3", req: 3200, d: "210ms" },
|
{ id: "minimax-m3", name: "MiniMax M3", req: 3200, d: "210ms" },
|
||||||
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", req: 3450, d: "270ms" },
|
{ id: "deepseek-v4-pro", name: "DeepSeek V4 Pro", req: 3450, d: "270ms" },
|
||||||
|
|
|
||||||
|
|
@ -313,7 +313,6 @@ export function LiteSection(props: { lite: LiteSubscription | undefined }) {
|
||||||
<li>Kimi K2.6</li>
|
<li>Kimi K2.6</li>
|
||||||
<li>MiniMax M3</li>
|
<li>MiniMax M3</li>
|
||||||
<li>MiniMax M2.7</li>
|
<li>MiniMax M2.7</li>
|
||||||
<li>Qwen3.8 Max</li>
|
|
||||||
<li>Qwen3.7 Max</li>
|
<li>Qwen3.7 Max</li>
|
||||||
<li>Qwen3.7 Plus</li>
|
<li>Qwen3.7 Plus</li>
|
||||||
<li>Qwen3.6 Plus</li>
|
<li>Qwen3.6 Plus</li>
|
||||||
|
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
CREATE UNIQUE INDEX `global_lite_subscription_id` ON `billing` (`lite_subscription_id`);--> statement-breakpoint
|
|
||||||
CREATE INDEX `referral_id` ON `referral_reward` (`referral_id`);
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -59,7 +59,30 @@ export namespace Referral {
|
||||||
const accountID = Actor.account()
|
const accountID = Actor.account()
|
||||||
const code = await ensureCode(workspaceID)
|
const code = await ensureCode(workspaceID)
|
||||||
const rows = await Database.use(async (tx) => {
|
const rows = await Database.use(async (tx) => {
|
||||||
const [invites, inviteeReferral] = await Promise.all([
|
const [rewards, invites, inviteeReferral, inviteeRewards] = await Promise.all([
|
||||||
|
tx
|
||||||
|
.select({
|
||||||
|
referralID: ReferralRewardTable.referralID,
|
||||||
|
workspaceID: ReferralRewardTable.workspaceID,
|
||||||
|
referralWorkspaceID: ReferralTable.workspaceID,
|
||||||
|
inviteeEmail: AuthTable.subject,
|
||||||
|
amount: ReferralRewardTable.amount,
|
||||||
|
timeCreated: ReferralRewardTable.timeCreated,
|
||||||
|
timeApplied: ReferralRewardTable.timeApplied,
|
||||||
|
})
|
||||||
|
.from(ReferralRewardTable)
|
||||||
|
.innerJoin(ReferralTable, eq(ReferralTable.id, ReferralRewardTable.referralID))
|
||||||
|
.innerJoin(
|
||||||
|
AuthTable,
|
||||||
|
and(eq(AuthTable.accountID, ReferralTable.inviteeAccountID), eq(AuthTable.provider, "email")),
|
||||||
|
)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(ReferralRewardTable.workspaceID, workspaceID),
|
||||||
|
isNull(ReferralRewardTable.timeDeleted),
|
||||||
|
isNull(ReferralTable.timeDeleted),
|
||||||
|
),
|
||||||
|
),
|
||||||
tx
|
tx
|
||||||
.select({ id: ReferralTable.id, inviteeEmail: AuthTable.subject, timeCreated: ReferralTable.timeCreated })
|
.select({ id: ReferralTable.id, inviteeEmail: AuthTable.subject, timeCreated: ReferralTable.timeCreated })
|
||||||
.from(ReferralTable)
|
.from(ReferralTable)
|
||||||
|
|
@ -83,20 +106,19 @@ export namespace Referral {
|
||||||
.where(and(eq(ReferralTable.inviteeAccountID, accountID), isNull(ReferralTable.timeDeleted)))
|
.where(and(eq(ReferralTable.inviteeAccountID, accountID), isNull(ReferralTable.timeDeleted)))
|
||||||
.orderBy(asc(UserTable.timeCreated))
|
.orderBy(asc(UserTable.timeCreated))
|
||||||
.then((rows) => rows.find((row) => row.inviterEmail) ?? rows[0]),
|
.then((rows) => rows.find((row) => row.inviterEmail) ?? rows[0]),
|
||||||
|
tx
|
||||||
|
.select({ referralID: ReferralRewardTable.referralID })
|
||||||
|
.from(ReferralRewardTable)
|
||||||
|
.innerJoin(ReferralTable, eq(ReferralTable.id, ReferralRewardTable.referralID))
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(ReferralTable.inviteeAccountID, accountID),
|
||||||
|
isNull(ReferralRewardTable.timeDeleted),
|
||||||
|
isNull(ReferralTable.timeDeleted),
|
||||||
|
),
|
||||||
|
),
|
||||||
])
|
])
|
||||||
|
|
||||||
// Hide reward history until the referral_id index can be deployed and this lookup restored.
|
|
||||||
const rewards: {
|
|
||||||
referralID: string
|
|
||||||
workspaceID: string
|
|
||||||
referralWorkspaceID: string
|
|
||||||
inviteeEmail: string
|
|
||||||
amount: number
|
|
||||||
timeCreated: Date
|
|
||||||
timeApplied: Date | null
|
|
||||||
}[] = []
|
|
||||||
// Hide pending invitee rewards until the referral_id index can be deployed and this lookup restored.
|
|
||||||
const inviteeRewards = inviteeReferral ? [{ referralID: inviteeReferral.id }] : []
|
|
||||||
return { inviteeReferral, inviteeRewards, invites, rewards }
|
return { inviteeReferral, inviteeRewards, invites, rewards }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@ export const BillingTable = mysqlTable(
|
||||||
...workspaceIndexes(table),
|
...workspaceIndexes(table),
|
||||||
uniqueIndex("global_customer_id").on(table.customerID),
|
uniqueIndex("global_customer_id").on(table.customerID),
|
||||||
uniqueIndex("global_subscription_id").on(table.subscriptionID),
|
uniqueIndex("global_subscription_id").on(table.subscriptionID),
|
||||||
uniqueIndex("global_lite_subscription_id").on(table.liteSubscriptionID),
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { bigint, index, mysqlTable, primaryKey, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
import { bigint, mysqlTable, primaryKey, uniqueIndex, varchar } from "drizzle-orm/mysql-core"
|
||||||
import { timestamps, ulid, utc, workspaceColumns } from "../drizzle/types"
|
import { timestamps, ulid, utc, workspaceColumns } from "../drizzle/types"
|
||||||
import { workspaceIndexes } from "./workspace.sql"
|
import { workspaceIndexes } from "./workspace.sql"
|
||||||
|
|
||||||
|
|
@ -31,8 +31,5 @@ export const ReferralRewardTable = mysqlTable(
|
||||||
amount: bigint("amount", { mode: "number" }).notNull(),
|
amount: bigint("amount", { mode: "number" }).notNull(),
|
||||||
timeApplied: utc("time_applied"),
|
timeApplied: utc("time_applied"),
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [primaryKey({ columns: [table.workspaceID, table.referralID] })],
|
||||||
primaryKey({ columns: [table.workspaceID, table.referralID] }),
|
|
||||||
index("referral_id").on(table.referralID),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,7 @@
|
||||||
"electron-log": "^5",
|
"electron-log": "^5",
|
||||||
"electron-store": "11.0.2",
|
"electron-store": "11.0.2",
|
||||||
"electron-updater": "6.8.9",
|
"electron-updater": "6.8.9",
|
||||||
"electron-window-state": "^5.0.3",
|
"electron-window-state": "^5.0.3"
|
||||||
"drizzle-orm": "catalog:"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@actions/artifact": "4.0.0",
|
"@actions/artifact": "4.0.0",
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
import { expect, test } from "bun:test"
|
|
||||||
import { createDesktopDraftStore } from "./draft-store"
|
|
||||||
|
|
||||||
test("flushes the latest buffered draft and stores blobs", () => {
|
|
||||||
const store = createDesktopDraftStore(":memory:")
|
|
||||||
store.set("prompt", "first")
|
|
||||||
store.set("prompt", "latest")
|
|
||||||
expect(store.get("prompt")).toBe("latest")
|
|
||||||
store.flush()
|
|
||||||
expect(store.get("prompt")).toBe("latest")
|
|
||||||
|
|
||||||
const bytes = new TextEncoder().encode("image")
|
|
||||||
const id = store.putBlob(bytes)
|
|
||||||
expect(store.getBlob(id)).toEqual(bytes)
|
|
||||||
store.close()
|
|
||||||
})
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
import { createHash } from "node:crypto"
|
|
||||||
import { DatabaseSync } from "node:sqlite"
|
|
||||||
import { eq } from "drizzle-orm"
|
|
||||||
import { drizzle } from "drizzle-orm/node-sqlite"
|
|
||||||
import { blob, sqliteTable, text } from "drizzle-orm/sqlite-core"
|
|
||||||
|
|
||||||
const documents = sqliteTable("document", {
|
|
||||||
key: text().primaryKey(),
|
|
||||||
value: text().notNull(),
|
|
||||||
})
|
|
||||||
const blobs = sqliteTable("blob", {
|
|
||||||
id: text().primaryKey(),
|
|
||||||
data: blob({ mode: "buffer" }).notNull(),
|
|
||||||
})
|
|
||||||
|
|
||||||
export function createDesktopDraftStore(filename: string) {
|
|
||||||
const native = new DatabaseSync(filename)
|
|
||||||
native.exec(
|
|
||||||
"PRAGMA journal_mode=WAL; CREATE TABLE IF NOT EXISTS document (key TEXT PRIMARY KEY, value TEXT NOT NULL); CREATE TABLE IF NOT EXISTS blob (id TEXT PRIMARY KEY, data BLOB NOT NULL);",
|
|
||||||
)
|
|
||||||
const db = drizzle({ client: native })
|
|
||||||
const used = new Set<string>()
|
|
||||||
db.select({ value: documents.value })
|
|
||||||
.from(documents)
|
|
||||||
.all()
|
|
||||||
.forEach(({ value }) =>
|
|
||||||
JSON.parse(value, (_key, item) => {
|
|
||||||
if (item?.blob && typeof item.blob.id === "string") used.add(item.blob.id)
|
|
||||||
return item
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
db.select({ id: blobs.id })
|
|
||||||
.from(blobs)
|
|
||||||
.all()
|
|
||||||
.filter(({ id }) => !used.has(id))
|
|
||||||
.forEach(({ id }) => db.delete(blobs).where(eq(blobs.id, id)).run())
|
|
||||||
const pending = new Map<string, string | null>()
|
|
||||||
let timer: ReturnType<typeof setTimeout> | undefined
|
|
||||||
const flush = () => {
|
|
||||||
if (timer) clearTimeout(timer)
|
|
||||||
timer = undefined
|
|
||||||
const writes = [...pending]
|
|
||||||
pending.clear()
|
|
||||||
db.transaction((tx) => {
|
|
||||||
writes.forEach(([key, value]) => {
|
|
||||||
if (value === null) tx.delete(documents).where(eq(documents.key, key)).run()
|
|
||||||
else
|
|
||||||
tx.insert(documents)
|
|
||||||
.values({ key, value })
|
|
||||||
.onConflictDoUpdate({ target: documents.key, set: { value } })
|
|
||||||
.run()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const schedule = () => {
|
|
||||||
if (!timer) timer = setTimeout(flush, 500)
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
get: (key: string) =>
|
|
||||||
pending.has(key)
|
|
||||||
? (pending.get(key) ?? null)
|
|
||||||
: (db.select({ value: documents.value }).from(documents).where(eq(documents.key, key)).get()?.value ?? null),
|
|
||||||
set(key: string, value: string | null) {
|
|
||||||
pending.set(key, value)
|
|
||||||
schedule()
|
|
||||||
},
|
|
||||||
putBlob(data: Uint8Array) {
|
|
||||||
const id = createHash("sha256").update(data).digest("hex")
|
|
||||||
db.insert(blobs)
|
|
||||||
.values({ id, data: Buffer.from(data) })
|
|
||||||
.onConflictDoNothing()
|
|
||||||
.run()
|
|
||||||
return id
|
|
||||||
},
|
|
||||||
getBlob: (id: string) => db.select({ data: blobs.data }).from(blobs).where(eq(blobs.id, id)).get()?.data ?? null,
|
|
||||||
flush,
|
|
||||||
close() {
|
|
||||||
flush()
|
|
||||||
native.close()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -171,7 +171,7 @@ const main = Effect.gen(function* () {
|
||||||
setAppQuitting()
|
setAppQuitting()
|
||||||
void stopSidecars().finally(() => {
|
void stopSidecars().finally(() => {
|
||||||
app.relaunch()
|
app.relaunch()
|
||||||
app.quit()
|
app.exit(0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,7 +245,7 @@ const main = Effect.gen(function* () {
|
||||||
for (const signal of ["SIGINT", "SIGTERM"] as const) {
|
for (const signal of ["SIGINT", "SIGTERM"] as const) {
|
||||||
process.on(signal, () => {
|
process.on(signal, () => {
|
||||||
setAppQuitting()
|
setAppQuitting()
|
||||||
void stopSidecars().finally(() => app.quit())
|
void stopSidecars().finally(() => app.exit(0))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { execFile } from "node:child_process"
|
import { execFile } from "node:child_process"
|
||||||
import { stat } from "node:fs/promises"
|
import { stat } from "node:fs/promises"
|
||||||
import { basename, join } from "node:path"
|
import { basename } from "node:path"
|
||||||
import { app, BrowserWindow, clipboard, dialog, ipcMain, shell } from "electron"
|
import { app, BrowserWindow, clipboard, dialog, ipcMain, shell } from "electron"
|
||||||
import type { IpcMainEvent, IpcMainInvokeEvent } from "electron"
|
import type { IpcMainEvent, IpcMainInvokeEvent } from "electron"
|
||||||
import type { DesktopMenuAction } from "@opencode-ai/app/desktop-menu"
|
import type { DesktopMenuAction } from "@opencode-ai/app/desktop-menu"
|
||||||
|
|
@ -21,7 +21,6 @@ import {
|
||||||
} from "./windows"
|
} from "./windows"
|
||||||
import type { UpdaterController } from "./updater-controller"
|
import type { UpdaterController } from "./updater-controller"
|
||||||
import { createUpdaterSubscriptions } from "./updater-subscriptions"
|
import { createUpdaterSubscriptions } from "./updater-subscriptions"
|
||||||
import { createDesktopDraftStore } from "./draft-store"
|
|
||||||
|
|
||||||
const pickerFilters = (ext?: string[]) => {
|
const pickerFilters = (ext?: string[]) => {
|
||||||
if (!ext || ext.length === 0) return undefined
|
if (!ext || ext.length === 0) return undefined
|
||||||
|
|
@ -52,12 +51,8 @@ type Deps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerIpcHandlers(deps: Deps) {
|
export function registerIpcHandlers(deps: Deps) {
|
||||||
const drafts = createDesktopDraftStore(join(app.getPath("userData"), "drafts.sqlite"))
|
|
||||||
const updaterSubscriptions = createUpdaterSubscriptions()
|
const updaterSubscriptions = createUpdaterSubscriptions()
|
||||||
app.once("will-quit", updaterSubscriptions.clear)
|
app.once("will-quit", updaterSubscriptions.clear)
|
||||||
app.on("before-quit", () => drafts.flush())
|
|
||||||
app.once("will-quit", () => drafts.close())
|
|
||||||
app.on("browser-window-created", (_event, win) => win.on("session-end", () => drafts.flush()))
|
|
||||||
|
|
||||||
ipcMain.handle("kill-sidecar", () => deps.killSidecar())
|
ipcMain.handle("kill-sidecar", () => deps.killSidecar())
|
||||||
ipcMain.handle("await-initialization", () => deps.awaitInitialization())
|
ipcMain.handle("await-initialization", () => deps.awaitInitialization())
|
||||||
|
|
@ -128,14 +123,6 @@ export function registerIpcHandlers(deps: Deps) {
|
||||||
const store = getStore(name)
|
const store = getStore(name)
|
||||||
return Object.keys(store.store).length
|
return Object.keys(store.store).length
|
||||||
})
|
})
|
||||||
ipcMain.handle("draft-get", (_event, key: string) => drafts.get(key))
|
|
||||||
ipcMain.handle("draft-set", (_event, key: string, value: string) => drafts.set(key, value))
|
|
||||||
ipcMain.handle("draft-delete", (_event, key: string) => drafts.set(key, null))
|
|
||||||
ipcMain.handle("draft-blob-put", (_event, data: ArrayBuffer) => drafts.putBlob(new Uint8Array(data)))
|
|
||||||
ipcMain.handle("draft-blob-get", (_event, id: string) => {
|
|
||||||
const data = drafts.getBlob(id)
|
|
||||||
return data ? data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) : null
|
|
||||||
})
|
|
||||||
|
|
||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
"open-directory-picker",
|
"open-directory-picker",
|
||||||
|
|
|
||||||
|
|
@ -73,11 +73,6 @@ const api: ElectronAPI = {
|
||||||
storeClear: (name) => ipcRenderer.invoke("store-clear", name),
|
storeClear: (name) => ipcRenderer.invoke("store-clear", name),
|
||||||
storeKeys: (name) => ipcRenderer.invoke("store-keys", name),
|
storeKeys: (name) => ipcRenderer.invoke("store-keys", name),
|
||||||
storeLength: (name) => ipcRenderer.invoke("store-length", name),
|
storeLength: (name) => ipcRenderer.invoke("store-length", name),
|
||||||
draftGet: (key) => ipcRenderer.invoke("draft-get", key),
|
|
||||||
draftSet: (key, value) => ipcRenderer.invoke("draft-set", key, value),
|
|
||||||
draftDelete: (key) => ipcRenderer.invoke("draft-delete", key),
|
|
||||||
draftBlobPut: (data) => ipcRenderer.invoke("draft-blob-put", data),
|
|
||||||
draftBlobGet: (id) => ipcRenderer.invoke("draft-blob-get", id),
|
|
||||||
|
|
||||||
getWindowID: () => ipcRenderer.invoke("get-window-id"),
|
getWindowID: () => ipcRenderer.invoke("get-window-id"),
|
||||||
onMenuCommand: (cb) => {
|
onMenuCommand: (cb) => {
|
||||||
|
|
|
||||||
|
|
@ -63,11 +63,6 @@ export type ElectronAPI = {
|
||||||
storeClear: (name: string) => Promise<void>
|
storeClear: (name: string) => Promise<void>
|
||||||
storeKeys: (name: string) => Promise<string[]>
|
storeKeys: (name: string) => Promise<string[]>
|
||||||
storeLength: (name: string) => Promise<number>
|
storeLength: (name: string) => Promise<number>
|
||||||
draftGet: (key: string) => Promise<string | null>
|
|
||||||
draftSet: (key: string, value: string) => Promise<void>
|
|
||||||
draftDelete: (key: string) => Promise<void>
|
|
||||||
draftBlobPut: (data: ArrayBuffer) => Promise<string>
|
|
||||||
draftBlobGet: (id: string) => Promise<ArrayBuffer | null>
|
|
||||||
|
|
||||||
getWindowID: () => Promise<string>
|
getWindowID: () => Promise<string>
|
||||||
onMenuCommand: (cb: (id: string) => void) => () => void
|
onMenuCommand: (cb: (id: string) => void) => () => void
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import {
|
||||||
type Locale,
|
type Locale,
|
||||||
type Platform,
|
type Platform,
|
||||||
PlatformProvider,
|
PlatformProvider,
|
||||||
createDraftStore,
|
|
||||||
ServerConnection,
|
ServerConnection,
|
||||||
useCommand,
|
useCommand,
|
||||||
useWslServers,
|
useWslServers,
|
||||||
|
|
@ -227,13 +226,6 @@ const createPlatform = (windowState: DesktopWindowState): Platform => {
|
||||||
},
|
},
|
||||||
|
|
||||||
storage,
|
storage,
|
||||||
draftStore: createDraftStore({
|
|
||||||
get: window.api.draftGet,
|
|
||||||
set: window.api.draftSet,
|
|
||||||
remove: window.api.draftDelete,
|
|
||||||
putBlob: (blob) => blob.arrayBuffer().then(window.api.draftBlobPut),
|
|
||||||
getBlob: (id) => window.api.draftBlobGet(id).then((data) => data && new Blob([data])),
|
|
||||||
}),
|
|
||||||
|
|
||||||
updater: {
|
updater: {
|
||||||
state: updaterState,
|
state: updaterState,
|
||||||
|
|
|
||||||
|
|
@ -340,6 +340,16 @@ export async function CodexAuthPlugin(input: PluginInput, options: CodexAuthPlug
|
||||||
return {
|
return {
|
||||||
apiKey: OAUTH_DUMMY_KEY,
|
apiKey: OAUTH_DUMMY_KEY,
|
||||||
async fetch(requestInput: RequestInfo | URL, init?: RequestInit) {
|
async fetch(requestInput: RequestInfo | URL, init?: RequestInit) {
|
||||||
|
const currentAuth = await getAuth()
|
||||||
|
if (currentAuth?.type === "api") {
|
||||||
|
const headers = new Headers(init?.headers)
|
||||||
|
headers.set("authorization", `Bearer ${currentAuth.key}`)
|
||||||
|
const requestInit = { ...init, headers }
|
||||||
|
return websocketFetch ? websocketFetch(requestInput, requestInit) : fetch(requestInput, requestInit)
|
||||||
|
}
|
||||||
|
if (currentAuth?.type !== "oauth")
|
||||||
|
return websocketFetch ? websocketFetch(requestInput, init) : fetch(requestInput, init)
|
||||||
|
|
||||||
if (init?.headers) {
|
if (init?.headers) {
|
||||||
if (init.headers instanceof Headers) {
|
if (init.headers instanceof Headers) {
|
||||||
init.headers.delete("authorization")
|
init.headers.delete("authorization")
|
||||||
|
|
@ -352,10 +362,6 @@ export async function CodexAuthPlugin(input: PluginInput, options: CodexAuthPlug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentAuth = await getAuth()
|
|
||||||
if (currentAuth.type !== "oauth")
|
|
||||||
return websocketFetch ? websocketFetch(requestInput, init) : fetch(requestInput, init)
|
|
||||||
|
|
||||||
const authWithAccount = currentAuth as typeof currentAuth & { accountId?: string }
|
const authWithAccount = currentAuth as typeof currentAuth & { accountId?: string }
|
||||||
|
|
||||||
if (!currentAuth.access || currentAuth.expires < Date.now()) {
|
if (!currentAuth.access || currentAuth.expires < Date.now()) {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import {
|
||||||
renderOAuthError,
|
renderOAuthError,
|
||||||
type IdTokenClaims,
|
type IdTokenClaims,
|
||||||
} from "../../src/plugin/openai/codex"
|
} from "../../src/plugin/openai/codex"
|
||||||
|
import { OAUTH_DUMMY_KEY } from "../../src/auth"
|
||||||
|
|
||||||
function createTestJwt(payload: object): string {
|
function createTestJwt(payload: object): string {
|
||||||
const header = Buffer.from(JSON.stringify({ alg: "none" })).toString("base64url")
|
const header = Buffer.from(JSON.stringify({ alg: "none" })).toString("base64url")
|
||||||
|
|
@ -149,6 +150,68 @@ describe("plugin.codex", () => {
|
||||||
await enabled.dispose?.()
|
await enabled.dispose?.()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("passes requests through when OAuth auth is removed after loading", async () => {
|
||||||
|
let auth:
|
||||||
|
| {
|
||||||
|
type: "oauth"
|
||||||
|
refresh: string
|
||||||
|
access: string
|
||||||
|
expires: number
|
||||||
|
}
|
||||||
|
| undefined = {
|
||||||
|
type: "oauth",
|
||||||
|
refresh: "refresh",
|
||||||
|
access: "access",
|
||||||
|
expires: Date.now() + 60_000,
|
||||||
|
}
|
||||||
|
using server = Bun.serve({
|
||||||
|
port: 0,
|
||||||
|
fetch(request) {
|
||||||
|
return Response.json({ authorization: request.headers.get("authorization") })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const hooks = await CodexAuthPlugin({} as never)
|
||||||
|
const loaded = await hooks.auth!.loader!(async () => auth as never, {} as never)
|
||||||
|
auth = undefined
|
||||||
|
|
||||||
|
const response = await loaded.fetch!(server.url, {
|
||||||
|
headers: { authorization: `Bearer ${OAUTH_DUMMY_KEY}` },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(await response.json()).toEqual({ authorization: `Bearer ${OAUTH_DUMMY_KEY}` })
|
||||||
|
})
|
||||||
|
|
||||||
|
test("uses current API key when OAuth auth is replaced after loading", async () => {
|
||||||
|
let auth:
|
||||||
|
| {
|
||||||
|
type: "oauth"
|
||||||
|
refresh: string
|
||||||
|
access: string
|
||||||
|
expires: number
|
||||||
|
}
|
||||||
|
| { type: "api"; key: string } = {
|
||||||
|
type: "oauth",
|
||||||
|
refresh: "refresh",
|
||||||
|
access: "access",
|
||||||
|
expires: Date.now() + 60_000,
|
||||||
|
}
|
||||||
|
using server = Bun.serve({
|
||||||
|
port: 0,
|
||||||
|
fetch(request) {
|
||||||
|
return Response.json({ authorization: request.headers.get("authorization") })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const hooks = await CodexAuthPlugin({} as never)
|
||||||
|
const loaded = await hooks.auth!.loader!(async () => auth as never, {} as never)
|
||||||
|
auth = { type: "api", key: "sk-current" }
|
||||||
|
|
||||||
|
const response = await loaded.fetch!(server.url, {
|
||||||
|
headers: { authorization: `Bearer ${OAUTH_DUMMY_KEY}` },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(await response.json()).toEqual({ authorization: "Bearer sk-current" })
|
||||||
|
})
|
||||||
|
|
||||||
test("filters unsupported modes and uses Codex context limits for OAuth GPT models", async () => {
|
test("filters unsupported modes and uses Codex context limits for OAuth GPT models", async () => {
|
||||||
const hooks = await CodexAuthPlugin({} as never)
|
const hooks = await CodexAuthPlugin({} as never)
|
||||||
const limit = { context: 1_050_000, input: 922_000, output: 128_000 }
|
const limit = { context: 1_050_000, input: 922_000, output: 128_000 }
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,6 @@ export type PromptInputV2AttachmentConfig = {
|
||||||
onError: (error: unknown) => void
|
onError: (error: unknown) => void
|
||||||
readClipboardImage?: () => Promise<File | null>
|
readClipboardImage?: () => Promise<File | null>
|
||||||
getPathForFile?: (file: File) => string
|
getPathForFile?: (file: File) => string
|
||||||
store?: (file: File) => Promise<{ id: string; url: string }>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPromptInputV2Attachments(
|
export function createPromptInputV2Attachments(
|
||||||
|
|
@ -103,7 +102,8 @@ export function createPromptInputV2Attachments(
|
||||||
if (toast) input.warn()
|
if (toast) input.warn()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const blob = input.store ? await input.store(file) : await blobReference(file)
|
const url = await dataUrl(file, mime)
|
||||||
|
if (!url) return false
|
||||||
const sourcePath = input.getPathForFile?.(file) || undefined
|
const sourcePath = input.getPathForFile?.(file) || undefined
|
||||||
// Native clipboard images arrive with a fresh timestamped filename on every paste, so identical
|
// Native clipboard images arrive with a fresh timestamped filename on every paste, so identical
|
||||||
// clipboard content is matched on bytes alone.
|
// clipboard content is matched on bytes alone.
|
||||||
|
|
@ -112,7 +112,7 @@ export function createPromptInputV2Attachments(
|
||||||
.some(
|
.some(
|
||||||
(part) =>
|
(part) =>
|
||||||
part.type === "image" &&
|
part.type === "image" &&
|
||||||
part.blob.id === blob.id &&
|
part.dataUrl === url &&
|
||||||
(sourcePath
|
(sourcePath
|
||||||
? part.sourcePath === sourcePath
|
? part.sourcePath === sourcePath
|
||||||
: !part.sourcePath && (clipboard || part.filename === file.name)),
|
: !part.sourcePath && (clipboard || part.filename === file.name)),
|
||||||
|
|
@ -127,7 +127,7 @@ export function createPromptInputV2Attachments(
|
||||||
filename: file.name,
|
filename: file.name,
|
||||||
sourcePath,
|
sourcePath,
|
||||||
mime,
|
mime,
|
||||||
blob,
|
dataUrl: url,
|
||||||
}
|
}
|
||||||
target.prompt.set([...target.prompt.current(), attachment], target.cursor)
|
target.prompt.set([...target.prompt.current(), attachment], target.cursor)
|
||||||
return true
|
return true
|
||||||
|
|
@ -219,14 +219,20 @@ export function createPromptInputV2Attachments(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageMimes = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"])
|
function dataUrl(file: File, mime: string) {
|
||||||
|
return new Promise<string>((resolve) => {
|
||||||
async function blobReference(file: File) {
|
const reader = new FileReader()
|
||||||
const id = Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", await file.arrayBuffer())))
|
reader.addEventListener("error", () => resolve(""))
|
||||||
.map((byte) => byte.toString(16).padStart(2, "0"))
|
reader.addEventListener("load", () => {
|
||||||
.join("")
|
const value = typeof reader.result === "string" ? reader.result : ""
|
||||||
return { id, url: URL.createObjectURL(file) }
|
const index = value.indexOf(",")
|
||||||
|
resolve(index === -1 ? value : `data:${mime};base64,${value.slice(index + 1)}`)
|
||||||
|
})
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const imageMimes = new Set(["image/png", "image/jpeg", "image/gif", "image/webp"])
|
||||||
const imageExtensions = new Map([
|
const imageExtensions = new Map([
|
||||||
["gif", "image/gif"],
|
["gif", "image/gif"],
|
||||||
["jpeg", "image/jpeg"],
|
["jpeg", "image/jpeg"],
|
||||||
|
|
|
||||||
|
|
@ -425,7 +425,7 @@ export function PromptInputV2Attachments(props: {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={attachment.blob.url}
|
src={attachment.dataUrl}
|
||||||
alt={attachment.filename}
|
alt={attachment.filename}
|
||||||
class="w-[58px] h-[46px] rounded-[6px] object-cover"
|
class="w-[58px] h-[46px] rounded-[6px] object-cover"
|
||||||
onClick={() => props.onAttachmentClick?.(attachment)}
|
onClick={() => props.onAttachmentClick?.(attachment)}
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ function ControlledPromptInput() {
|
||||||
id: "attachment-1",
|
id: "attachment-1",
|
||||||
filename: "requirements.md",
|
filename: "requirements.md",
|
||||||
mime: "text/markdown",
|
mime: "text/markdown",
|
||||||
blob: { id: "requirements", url: "data:text/markdown;base64,IyBSZXF1aXJlbWVudHM=" },
|
dataUrl: "data:text/markdown;base64,IyBSZXF1aXJlbWVudHM=",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
cursor: 0,
|
cursor: 0,
|
||||||
|
|
@ -199,7 +199,7 @@ function ControlledPromptInput() {
|
||||||
id: `attachment-${store.state.prompt.filter((part) => part.type === "image").length + 1}`,
|
id: `attachment-${store.state.prompt.filter((part) => part.type === "image").length + 1}`,
|
||||||
filename,
|
filename,
|
||||||
mime,
|
mime,
|
||||||
blob: { id: filename, url: `data:${mime};base64,` },
|
dataUrl: `data:${mime};base64,`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ function createPromptStore() {
|
||||||
id: "attachment-1",
|
id: "attachment-1",
|
||||||
filename: "notes.txt",
|
filename: "notes.txt",
|
||||||
mime: "text/plain",
|
mime: "text/plain",
|
||||||
blob: { id: "a", url: "blob:a" },
|
dataUrl: "data:text/plain;base64,",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
cursor: 3,
|
cursor: 3,
|
||||||
|
|
@ -50,7 +50,7 @@ describe("prompt input v2 store", () => {
|
||||||
id: "attachment-1",
|
id: "attachment-1",
|
||||||
filename: "notes.txt",
|
filename: "notes.txt",
|
||||||
mime: "text/plain",
|
mime: "text/plain",
|
||||||
blob: { id: "a", url: "blob:a" },
|
dataUrl: "data:text/plain;base64,",
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
expect(prompt.state.cursor).toBe(7)
|
expect(prompt.state.cursor).toBe(7)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ export type PromptInputV2Attachment = {
|
||||||
filename: string
|
filename: string
|
||||||
sourcePath?: string
|
sourcePath?: string
|
||||||
mime: string
|
mime: string
|
||||||
blob: { id: string; url: string }
|
dataUrl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PromptInputV2Prompt = (
|
export type PromptInputV2Prompt = (
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ OpenCode Go هو اشتراك منخفض التكلفة — **$5 للشهر ال
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -97,8 +96,7 @@ OpenCode Go هو اشتراك منخفض التكلفة — **$5 للشهر ال
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -116,7 +114,6 @@ OpenCode Go هو اشتراك منخفض التكلفة — **$5 للشهر ال
|
||||||
- DeepSeek V4 Flash — 790 input، و68,000 cached، و280 output tokens لكل طلب
|
- DeepSeek V4 Flash — 790 input، و68,000 cached، و280 output tokens لكل طلب
|
||||||
- MiniMax M3 — 510 input، و56,000 cached، و190 output tokens لكل طلب
|
- MiniMax M3 — 510 input، و56,000 cached، و190 output tokens لكل طلب
|
||||||
- MiniMax M2.7 — 300 input، و55,000 cached، و125 output tokens لكل طلب
|
- MiniMax M2.7 — 300 input، و55,000 cached، و125 output tokens لكل طلب
|
||||||
- Qwen3.8 Max — 420 input، و66,000 cached، و200 output tokens لكل طلب
|
|
||||||
- Qwen3.7 Max — 420 input، و66,000 cached، و200 output tokens لكل طلب
|
- Qwen3.7 Max — 420 input، و66,000 cached، و200 output tokens لكل طلب
|
||||||
- Qwen3.7 Plus — 500 input، و57,000 cached، و190 output tokens لكل طلب
|
- Qwen3.7 Plus — 500 input، و57,000 cached، و190 output tokens لكل طلب
|
||||||
- Qwen3.6 Plus — 500 input، و57,000 cached، و190 output tokens لكل طلب
|
- Qwen3.6 Plus — 500 input، و57,000 cached، و190 output tokens لكل طلب
|
||||||
|
|
@ -141,7 +138,6 @@ OpenCode Go هو اشتراك منخفض التكلفة — **$5 للشهر ال
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -199,7 +195,6 @@ OpenCode Go هو اشتراك منخفض التكلفة — **$5 للشهر ال
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -232,7 +227,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | غير مستخدَمة | 0 أيام |
|
| Kimi K2.6 | غير مستخدَمة | 0 أيام |
|
||||||
| MiMo-V2.5-Pro | غير مستخدَمة | 0 أيام |
|
| MiMo-V2.5-Pro | غير مستخدَمة | 0 أيام |
|
||||||
| MiMo-V2.5 | غير مستخدَمة | 0 أيام |
|
| MiMo-V2.5 | غير مستخدَمة | 0 أيام |
|
||||||
| Qwen3.8 Max | غير مستخدَمة | 0 أيام |
|
|
||||||
| Qwen3.7 Max | غير مستخدَمة | 0 أيام |
|
| Qwen3.7 Max | غير مستخدَمة | 0 أيام |
|
||||||
| Qwen3.7 Plus | غير مستخدَمة | 0 أيام |
|
| Qwen3.7 Plus | غير مستخدَمة | 0 أيام |
|
||||||
| Qwen3.6 Plus | غير مستخدَمة | 0 أيام |
|
| Qwen3.6 Plus | غير مستخدَمة | 0 أيام |
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ Trenutna lista modela uključuje:
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -107,8 +106,7 @@ Tabela ispod pruža procijenjeni broj zahtjeva na osnovu tipičnih obrazaca kori
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -126,7 +124,6 @@ Procjene se zasnivaju na zapaženim obrascima zahtjeva:
|
||||||
- DeepSeek V4 Flash — 790 ulaznih, 68,000 keširanih, 280 izlaznih tokena po zahtjevu
|
- DeepSeek V4 Flash — 790 ulaznih, 68,000 keširanih, 280 izlaznih tokena po zahtjevu
|
||||||
- MiniMax M3 — 510 ulaznih, 56,000 keširanih, 190 izlaznih tokena po zahtjevu
|
- MiniMax M3 — 510 ulaznih, 56,000 keširanih, 190 izlaznih tokena po zahtjevu
|
||||||
- MiniMax M2.7 — 300 ulaznih, 55,000 keširanih, 125 izlaznih tokena po zahtjevu
|
- MiniMax M2.7 — 300 ulaznih, 55,000 keširanih, 125 izlaznih tokena po zahtjevu
|
||||||
- Qwen3.8 Max — 420 ulaznih, 66,000 keširanih, 200 izlaznih tokena po zahtjevu
|
|
||||||
- Qwen3.7 Max — 420 ulaznih, 66,000 keširanih, 200 izlaznih tokena po zahtjevu
|
- Qwen3.7 Max — 420 ulaznih, 66,000 keširanih, 200 izlaznih tokena po zahtjevu
|
||||||
- Qwen3.7 Plus — 500 ulaznih, 57,000 keširanih, 190 izlaznih tokena po zahtjevu
|
- Qwen3.7 Plus — 500 ulaznih, 57,000 keširanih, 190 izlaznih tokena po zahtjevu
|
||||||
- Qwen3.6 Plus — 500 ulaznih, 57,000 keširanih, 190 izlaznih tokena po zahtjevu
|
- Qwen3.6 Plus — 500 ulaznih, 57,000 keširanih, 190 izlaznih tokena po zahtjevu
|
||||||
|
|
@ -151,7 +148,6 @@ Procjene se također zasnivaju na sljedećim cijenama po 1M tokena i mjesečnoj
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -211,7 +207,6 @@ Također možete pristupiti Go modelima putem sljedećih API endpointa.
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -246,7 +241,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | Ne koristi se | 0 dana |
|
| Kimi K2.6 | Ne koristi se | 0 dana |
|
||||||
| MiMo-V2.5-Pro | Ne koristi se | 0 dana |
|
| MiMo-V2.5-Pro | Ne koristi se | 0 dana |
|
||||||
| MiMo-V2.5 | Ne koristi se | 0 dana |
|
| MiMo-V2.5 | Ne koristi se | 0 dana |
|
||||||
| Qwen3.8 Max | Ne koristi se | 0 dana |
|
|
||||||
| Qwen3.7 Max | Ne koristi se | 0 dana |
|
| Qwen3.7 Max | Ne koristi se | 0 dana |
|
||||||
| Qwen3.7 Plus | Ne koristi se | 0 dana |
|
| Qwen3.7 Plus | Ne koristi se | 0 dana |
|
||||||
| Qwen3.6 Plus | Ne koristi se | 0 dana |
|
| Qwen3.6 Plus | Ne koristi se | 0 dana |
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ Den nuværende liste over modeller inkluderer:
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -107,8 +106,7 @@ Tabellen nedenfor giver et estimeret antal anmodninger baseret på typiske Go-fo
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -126,7 +124,6 @@ Estimaterne er baseret på observerede anmodningsmønstre:
|
||||||
- DeepSeek V4 Flash — 790 input, 68.000 cachelagrede, 280 output-tokens pr. anmodning
|
- DeepSeek V4 Flash — 790 input, 68.000 cachelagrede, 280 output-tokens pr. anmodning
|
||||||
- MiniMax M3 — 510 input, 56.000 cachelagrede, 190 output-tokens pr. anmodning
|
- MiniMax M3 — 510 input, 56.000 cachelagrede, 190 output-tokens pr. anmodning
|
||||||
- MiniMax M2.7 — 300 input, 55.000 cachelagrede, 125 output-tokens pr. anmodning
|
- MiniMax M2.7 — 300 input, 55.000 cachelagrede, 125 output-tokens pr. anmodning
|
||||||
- Qwen3.8 Max — 420 input, 66.000 cachelagrede, 200 output-tokens pr. anmodning
|
|
||||||
- Qwen3.7 Max — 420 input, 66.000 cachelagrede, 200 output-tokens pr. anmodning
|
- Qwen3.7 Max — 420 input, 66.000 cachelagrede, 200 output-tokens pr. anmodning
|
||||||
- Qwen3.7 Plus — 500 input, 57.000 cachelagrede, 190 output-tokens pr. anmodning
|
- Qwen3.7 Plus — 500 input, 57.000 cachelagrede, 190 output-tokens pr. anmodning
|
||||||
- Qwen3.6 Plus — 500 input, 57.000 cachelagrede, 190 output-tokens pr. anmodning
|
- Qwen3.6 Plus — 500 input, 57.000 cachelagrede, 190 output-tokens pr. anmodning
|
||||||
|
|
@ -151,7 +148,6 @@ Estimaterne er også baseret på følgende priser pr. 1M tokens og det månedlig
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -211,7 +207,6 @@ Du kan også få adgang til Go-modeller gennem følgende API-endpoints.
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -246,7 +241,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | Ikke brugt | 0 dage |
|
| Kimi K2.6 | Ikke brugt | 0 dage |
|
||||||
| MiMo-V2.5-Pro | Ikke brugt | 0 dage |
|
| MiMo-V2.5-Pro | Ikke brugt | 0 dage |
|
||||||
| MiMo-V2.5 | Ikke brugt | 0 dage |
|
| MiMo-V2.5 | Ikke brugt | 0 dage |
|
||||||
| Qwen3.8 Max | Ikke brugt | 0 dage |
|
|
||||||
| Qwen3.7 Max | Ikke brugt | 0 dage |
|
| Qwen3.7 Max | Ikke brugt | 0 dage |
|
||||||
| Qwen3.7 Plus | Ikke brugt | 0 dage |
|
| Qwen3.7 Plus | Ikke brugt | 0 dage |
|
||||||
| Qwen3.6 Plus | Ikke brugt | 0 dage |
|
| Qwen3.6 Plus | Ikke brugt | 0 dage |
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,6 @@ Die aktuelle Liste der Modelle umfasst:
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -99,8 +98,7 @@ Die folgende Tabelle zeigt eine geschätzte Anzahl von Anfragen basierend auf ty
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -118,7 +116,6 @@ Die Schätzungen basieren auf beobachteten Anfragemustern:
|
||||||
- DeepSeek V4 Flash — 790 Input-, 68.000 Cached-, 280 Output-Tokens pro Anfrage
|
- DeepSeek V4 Flash — 790 Input-, 68.000 Cached-, 280 Output-Tokens pro Anfrage
|
||||||
- MiniMax M3 — 510 Input-, 56.000 Cached-, 190 Output-Tokens pro Anfrage
|
- MiniMax M3 — 510 Input-, 56.000 Cached-, 190 Output-Tokens pro Anfrage
|
||||||
- MiniMax M2.7 — 300 Input-, 55.000 Cached-, 125 Output-Tokens pro Anfrage
|
- MiniMax M2.7 — 300 Input-, 55.000 Cached-, 125 Output-Tokens pro Anfrage
|
||||||
- Qwen3.8 Max — 420 Input-, 66.000 Cached-, 200 Output-Tokens pro Anfrage
|
|
||||||
- Qwen3.7 Max — 420 Input-, 66.000 Cached-, 200 Output-Tokens pro Anfrage
|
- Qwen3.7 Max — 420 Input-, 66.000 Cached-, 200 Output-Tokens pro Anfrage
|
||||||
- Qwen3.7 Plus — 500 Input-, 57.000 Cached-, 190 Output-Tokens pro Anfrage
|
- Qwen3.7 Plus — 500 Input-, 57.000 Cached-, 190 Output-Tokens pro Anfrage
|
||||||
- Qwen3.6 Plus — 500 Input-, 57.000 Cached-, 190 Output-Tokens pro Anfrage
|
- Qwen3.6 Plus — 500 Input-, 57.000 Cached-, 190 Output-Tokens pro Anfrage
|
||||||
|
|
@ -143,7 +140,6 @@ Die Schätzungen basieren außerdem auf den folgenden Preisen pro 1M Tokens und
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -201,7 +197,6 @@ Du kannst auf die Go-Modelle auch über die folgenden API-Endpunkte zugreifen.
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -234,7 +229,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | Nicht verwendet | 0 Tage |
|
| Kimi K2.6 | Nicht verwendet | 0 Tage |
|
||||||
| MiMo-V2.5-Pro | Nicht verwendet | 0 Tage |
|
| MiMo-V2.5-Pro | Nicht verwendet | 0 Tage |
|
||||||
| MiMo-V2.5 | Nicht verwendet | 0 Tage |
|
| MiMo-V2.5 | Nicht verwendet | 0 Tage |
|
||||||
| Qwen3.8 Max | Nicht verwendet | 0 Tage |
|
|
||||||
| Qwen3.7 Max | Nicht verwendet | 0 Tage |
|
| Qwen3.7 Max | Nicht verwendet | 0 Tage |
|
||||||
| Qwen3.7 Plus | Nicht verwendet | 0 Tage |
|
| Qwen3.7 Plus | Nicht verwendet | 0 Tage |
|
||||||
| Qwen3.6 Plus | Nicht verwendet | 0 Tage |
|
| Qwen3.6 Plus | Nicht verwendet | 0 Tage |
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ La lista actual de modelos incluye:
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -107,8 +106,7 @@ La siguiente tabla proporciona una cantidad estimada de peticiones basada en los
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -126,7 +124,6 @@ Las estimaciones se basan en los patrones de peticiones observados:
|
||||||
- DeepSeek V4 Flash — 790 tokens de entrada, 68,000 en caché, 280 tokens de salida por petición
|
- DeepSeek V4 Flash — 790 tokens de entrada, 68,000 en caché, 280 tokens de salida por petición
|
||||||
- MiniMax M3 — 510 tokens de entrada, 56,000 en caché, 190 tokens de salida por petición
|
- MiniMax M3 — 510 tokens de entrada, 56,000 en caché, 190 tokens de salida por petición
|
||||||
- MiniMax M2.7 — 300 tokens de entrada, 55,000 en caché, 125 tokens de salida por petición
|
- MiniMax M2.7 — 300 tokens de entrada, 55,000 en caché, 125 tokens de salida por petición
|
||||||
- Qwen3.8 Max — 420 tokens de entrada, 66,000 en caché, 200 tokens de salida por petición
|
|
||||||
- Qwen3.7 Max — 420 tokens de entrada, 66,000 en caché, 200 tokens de salida por petición
|
- Qwen3.7 Max — 420 tokens de entrada, 66,000 en caché, 200 tokens de salida por petición
|
||||||
- Qwen3.7 Plus — 500 tokens de entrada, 57,000 en caché, 190 tokens de salida por petición
|
- Qwen3.7 Plus — 500 tokens de entrada, 57,000 en caché, 190 tokens de salida por petición
|
||||||
- Qwen3.6 Plus — 500 tokens de entrada, 57,000 en caché, 190 tokens de salida por petición
|
- Qwen3.6 Plus — 500 tokens de entrada, 57,000 en caché, 190 tokens de salida por petición
|
||||||
|
|
@ -151,7 +148,6 @@ Las estimaciones también se basan en los siguientes precios por 1M tokens y en
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -211,7 +207,6 @@ También puedes acceder a los modelos de Go a través de los siguientes endpoint
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -246,7 +241,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | No utilizado | 0 días |
|
| Kimi K2.6 | No utilizado | 0 días |
|
||||||
| MiMo-V2.5-Pro | No utilizado | 0 días |
|
| MiMo-V2.5-Pro | No utilizado | 0 días |
|
||||||
| MiMo-V2.5 | No utilizado | 0 días |
|
| MiMo-V2.5 | No utilizado | 0 días |
|
||||||
| Qwen3.8 Max | No utilizado | 0 días |
|
|
||||||
| Qwen3.7 Max | No utilizado | 0 días |
|
| Qwen3.7 Max | No utilizado | 0 días |
|
||||||
| Qwen3.7 Plus | No utilizado | 0 días |
|
| Qwen3.7 Plus | No utilizado | 0 días |
|
||||||
| Qwen3.6 Plus | No utilizado | 0 días |
|
| Qwen3.6 Plus | No utilizado | 0 días |
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ La liste actuelle des modèles comprend :
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -97,8 +96,7 @@ Le tableau ci-dessous fournit une estimation du nombre de requêtes basée sur d
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -116,7 +114,6 @@ Les estimations sont basées sur les schémas de requêtes observés :
|
||||||
- DeepSeek V4 Flash — 790 tokens en entrée, 68,000 en cache, 280 tokens en sortie par requête
|
- DeepSeek V4 Flash — 790 tokens en entrée, 68,000 en cache, 280 tokens en sortie par requête
|
||||||
- MiniMax M3 — 510 tokens en entrée, 56,000 en cache, 190 tokens en sortie par requête
|
- MiniMax M3 — 510 tokens en entrée, 56,000 en cache, 190 tokens en sortie par requête
|
||||||
- MiniMax M2.7 — 300 tokens en entrée, 55,000 en cache, 125 tokens en sortie par requête
|
- MiniMax M2.7 — 300 tokens en entrée, 55,000 en cache, 125 tokens en sortie par requête
|
||||||
- Qwen3.8 Max — 420 tokens en entrée, 66,000 en cache, 200 tokens en sortie par requête
|
|
||||||
- Qwen3.7 Max — 420 tokens en entrée, 66,000 en cache, 200 tokens en sortie par requête
|
- Qwen3.7 Max — 420 tokens en entrée, 66,000 en cache, 200 tokens en sortie par requête
|
||||||
- Qwen3.7 Plus — 500 tokens en entrée, 57,000 en cache, 190 tokens en sortie par requête
|
- Qwen3.7 Plus — 500 tokens en entrée, 57,000 en cache, 190 tokens en sortie par requête
|
||||||
- Qwen3.6 Plus — 500 tokens en entrée, 57,000 en cache, 190 tokens en sortie par requête
|
- Qwen3.6 Plus — 500 tokens en entrée, 57,000 en cache, 190 tokens en sortie par requête
|
||||||
|
|
@ -141,7 +138,6 @@ Les estimations sont également basées sur les prix suivants par 1M tokens et s
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -199,7 +195,6 @@ Vous pouvez également accéder aux modèles Go via les points de terminaison d'
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -232,7 +227,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | Non utilisé | 0 jour |
|
| Kimi K2.6 | Non utilisé | 0 jour |
|
||||||
| MiMo-V2.5-Pro | Non utilisé | 0 jour |
|
| MiMo-V2.5-Pro | Non utilisé | 0 jour |
|
||||||
| MiMo-V2.5 | Non utilisé | 0 jour |
|
| MiMo-V2.5 | Non utilisé | 0 jour |
|
||||||
| Qwen3.8 Max | Non utilisé | 0 jour |
|
|
||||||
| Qwen3.7 Max | Non utilisé | 0 jour |
|
| Qwen3.7 Max | Non utilisé | 0 jour |
|
||||||
| Qwen3.7 Plus | Non utilisé | 0 jour |
|
| Qwen3.7 Plus | Non utilisé | 0 jour |
|
||||||
| Qwen3.6 Plus | Non utilisé | 0 jour |
|
| Qwen3.6 Plus | Non utilisé | 0 jour |
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ The current list of models includes:
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -107,8 +106,7 @@ The table below provides an estimated request count based on typical Go usage pa
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -128,7 +126,6 @@ The estimates are based on observed request patterns:
|
||||||
- MiniMax M2.7 — 300 input, 55,000 cached, 125 output tokens per request
|
- MiniMax M2.7 — 300 input, 55,000 cached, 125 output tokens per request
|
||||||
- MiMo-V2.5 — 830 input, 71,500 cached, 295 output tokens per request
|
- MiMo-V2.5 — 830 input, 71,500 cached, 295 output tokens per request
|
||||||
- MiMo-V2.5-Pro — 790 input, 86,000 cached, 305 output tokens per request
|
- MiMo-V2.5-Pro — 790 input, 86,000 cached, 305 output tokens per request
|
||||||
- Qwen3.8 Max — 420 input, 66,000 cached, 200 output tokens per request
|
|
||||||
- Qwen3.7 Max — 420 input, 66,000 cached, 200 output tokens per request
|
- Qwen3.7 Max — 420 input, 66,000 cached, 200 output tokens per request
|
||||||
- Qwen3.7 Plus — 500 input, 57,000 cached, 190 output tokens per request
|
- Qwen3.7 Plus — 500 input, 57,000 cached, 190 output tokens per request
|
||||||
- Qwen3.6 Plus — 500 input, 57,000 cached, 190 output tokens per request
|
- Qwen3.6 Plus — 500 input, 57,000 cached, 190 output tokens per request
|
||||||
|
|
@ -151,7 +148,6 @@ The estimates are also based on the following prices per 1M tokens and the month
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -211,7 +207,6 @@ You can also access Go models through the following API endpoints.
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -246,7 +241,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | Not used | 0 days |
|
| Kimi K2.6 | Not used | 0 days |
|
||||||
| MiMo-V2.5-Pro | Not used | 0 days |
|
| MiMo-V2.5-Pro | Not used | 0 days |
|
||||||
| MiMo-V2.5 | Not used | 0 days |
|
| MiMo-V2.5 | Not used | 0 days |
|
||||||
| Qwen3.8 Max | Not used | 0 days |
|
|
||||||
| Qwen3.7 Max | Not used | 0 days |
|
| Qwen3.7 Max | Not used | 0 days |
|
||||||
| Qwen3.7 Plus | Not used | 0 days |
|
| Qwen3.7 Plus | Not used | 0 days |
|
||||||
| Qwen3.6 Plus | Not used | 0 days |
|
| Qwen3.6 Plus | Not used | 0 days |
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,6 @@ L'elenco attuale dei modelli include:
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -105,8 +104,7 @@ La tabella seguente fornisce una stima del conteggio delle richieste in base a p
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -124,7 +122,6 @@ Le stime si basano sui pattern di richieste osservati:
|
||||||
- DeepSeek V4 Flash — 790 di input, 68.000 in cache, 280 token di output per richiesta
|
- DeepSeek V4 Flash — 790 di input, 68.000 in cache, 280 token di output per richiesta
|
||||||
- MiniMax M3 — 510 di input, 56.000 in cache, 190 token di output per richiesta
|
- MiniMax M3 — 510 di input, 56.000 in cache, 190 token di output per richiesta
|
||||||
- MiniMax M2.7 — 300 di input, 55.000 in cache, 125 token di output per richiesta
|
- MiniMax M2.7 — 300 di input, 55.000 in cache, 125 token di output per richiesta
|
||||||
- Qwen3.8 Max — 420 di input, 66.000 in cache, 200 token di output per richiesta
|
|
||||||
- Qwen3.7 Max — 420 di input, 66.000 in cache, 200 token di output per richiesta
|
- Qwen3.7 Max — 420 di input, 66.000 in cache, 200 token di output per richiesta
|
||||||
- Qwen3.7 Plus — 500 di input, 57.000 in cache, 190 token di output per richiesta
|
- Qwen3.7 Plus — 500 di input, 57.000 in cache, 190 token di output per richiesta
|
||||||
- Qwen3.6 Plus — 500 di input, 57.000 in cache, 190 token di output per richiesta
|
- Qwen3.6 Plus — 500 di input, 57.000 in cache, 190 token di output per richiesta
|
||||||
|
|
@ -149,7 +146,6 @@ Le stime si basano anche sui seguenti prezzi per 1M token e sull'utilizzo mensil
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -209,7 +205,6 @@ Puoi anche accedere ai modelli Go tramite i seguenti endpoint API.
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -244,7 +239,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | Non utilizzato | 0 giorni |
|
| Kimi K2.6 | Non utilizzato | 0 giorni |
|
||||||
| MiMo-V2.5-Pro | Non utilizzato | 0 giorni |
|
| MiMo-V2.5-Pro | Non utilizzato | 0 giorni |
|
||||||
| MiMo-V2.5 | Non utilizzato | 0 giorni |
|
| MiMo-V2.5 | Non utilizzato | 0 giorni |
|
||||||
| Qwen3.8 Max | Non utilizzato | 0 giorni |
|
|
||||||
| Qwen3.7 Max | Non utilizzato | 0 giorni |
|
| Qwen3.7 Max | Non utilizzato | 0 giorni |
|
||||||
| Qwen3.7 Plus | Non utilizzato | 0 giorni |
|
| Qwen3.7 Plus | Non utilizzato | 0 giorni |
|
||||||
| Qwen3.6 Plus | Non utilizzato | 0 giorni |
|
| Qwen3.6 Plus | Non utilizzato | 0 giorni |
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ OpenCode Goをサブスクライブできるのは、1つのワークスペー
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -97,8 +96,7 @@ OpenCode Goには以下の制限が含まれています:
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -116,7 +114,6 @@ OpenCode Goには以下の制限が含まれています:
|
||||||
- DeepSeek V4 Flash — リクエストあたり 入力 790トークン、キャッシュ 68,000トークン、出力 280トークン
|
- DeepSeek V4 Flash — リクエストあたり 入力 790トークン、キャッシュ 68,000トークン、出力 280トークン
|
||||||
- MiniMax M3 — リクエストあたり 入力 510トークン、キャッシュ 56,000トークン、出力 190トークン
|
- MiniMax M3 — リクエストあたり 入力 510トークン、キャッシュ 56,000トークン、出力 190トークン
|
||||||
- MiniMax M2.7 — リクエストあたり 入力 300トークン、キャッシュ 55,000トークン、出力 125トークン
|
- MiniMax M2.7 — リクエストあたり 入力 300トークン、キャッシュ 55,000トークン、出力 125トークン
|
||||||
- Qwen3.8 Max — リクエストあたり 入力 420トークン、キャッシュ 66,000トークン、出力 200トークン
|
|
||||||
- Qwen3.7 Max — リクエストあたり 入力 420トークン、キャッシュ 66,000トークン、出力 200トークン
|
- Qwen3.7 Max — リクエストあたり 入力 420トークン、キャッシュ 66,000トークン、出力 200トークン
|
||||||
- Qwen3.7 Plus — リクエストあたり 入力 500トークン、キャッシュ 57,000トークン、出力 190トークン
|
- Qwen3.7 Plus — リクエストあたり 入力 500トークン、キャッシュ 57,000トークン、出力 190トークン
|
||||||
- Qwen3.6 Plus — リクエストあたり 入力 500トークン、キャッシュ 57,000トークン、出力 190トークン
|
- Qwen3.6 Plus — リクエストあたり 入力 500トークン、キャッシュ 57,000トークン、出力 190トークン
|
||||||
|
|
@ -141,7 +138,6 @@ OpenCode Goには以下の制限が含まれています:
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -199,7 +195,6 @@ Goでは月額$10を支払い、その6倍の利用枠を提供することを
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -232,7 +227,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | 使用なし | 0日 |
|
| Kimi K2.6 | 使用なし | 0日 |
|
||||||
| MiMo-V2.5-Pro | 使用なし | 0日 |
|
| MiMo-V2.5-Pro | 使用なし | 0日 |
|
||||||
| MiMo-V2.5 | 使用なし | 0日 |
|
| MiMo-V2.5 | 使用なし | 0日 |
|
||||||
| Qwen3.8 Max | 使用なし | 0日 |
|
|
||||||
| Qwen3.7 Max | 使用なし | 0日 |
|
| Qwen3.7 Max | 使用なし | 0日 |
|
||||||
| Qwen3.7 Plus | 使用なし | 0日 |
|
| Qwen3.7 Plus | 使用なし | 0日 |
|
||||||
| Qwen3.6 Plus | 使用なし | 0日 |
|
| Qwen3.6 Plus | 使用なし | 0日 |
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ workspace당 한 명의 멤버만 OpenCode Go를 구독할 수 있습니다.
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -97,8 +96,7 @@ OpenCode Go에는 다음과 같은 한도가 포함됩니다.
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -116,7 +114,6 @@ OpenCode Go에는 다음과 같은 한도가 포함됩니다.
|
||||||
- DeepSeek V4 Flash — 요청당 입력 790, 캐시 68,000, 출력 토큰 280
|
- DeepSeek V4 Flash — 요청당 입력 790, 캐시 68,000, 출력 토큰 280
|
||||||
- MiniMax M3 — 요청당 입력 510, 캐시 56,000, 출력 토큰 190
|
- MiniMax M3 — 요청당 입력 510, 캐시 56,000, 출력 토큰 190
|
||||||
- MiniMax M2.7 — 요청당 입력 300, 캐시 55,000, 출력 토큰 125
|
- MiniMax M2.7 — 요청당 입력 300, 캐시 55,000, 출력 토큰 125
|
||||||
- Qwen3.8 Max — 요청당 입력 420, 캐시 66,000, 출력 토큰 200
|
|
||||||
- Qwen3.7 Max — 요청당 입력 420, 캐시 66,000, 출력 토큰 200
|
- Qwen3.7 Max — 요청당 입력 420, 캐시 66,000, 출력 토큰 200
|
||||||
- Qwen3.7 Plus — 요청당 입력 500, 캐시 57,000, 출력 토큰 190
|
- Qwen3.7 Plus — 요청당 입력 500, 캐시 57,000, 출력 토큰 190
|
||||||
- Qwen3.6 Plus — 요청당 입력 500, 캐시 57,000, 출력 토큰 190
|
- Qwen3.6 Plus — 요청당 입력 500, 캐시 57,000, 출력 토큰 190
|
||||||
|
|
@ -141,7 +138,6 @@ OpenCode Go에는 다음과 같은 한도가 포함됩니다.
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -199,7 +195,6 @@ Go에서는 월 $10를 지불하며, 저희는 그 6배의 사용량을 제공
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -232,7 +227,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | 사용되지 않음 | 0일 |
|
| Kimi K2.6 | 사용되지 않음 | 0일 |
|
||||||
| MiMo-V2.5-Pro | 사용되지 않음 | 0일 |
|
| MiMo-V2.5-Pro | 사용되지 않음 | 0일 |
|
||||||
| MiMo-V2.5 | 사용되지 않음 | 0일 |
|
| MiMo-V2.5 | 사용되지 않음 | 0일 |
|
||||||
| Qwen3.8 Max | 사용되지 않음 | 0일 |
|
|
||||||
| Qwen3.7 Max | 사용되지 않음 | 0일 |
|
| Qwen3.7 Max | 사용되지 않음 | 0일 |
|
||||||
| Qwen3.7 Plus | 사용되지 않음 | 0일 |
|
| Qwen3.7 Plus | 사용되지 않음 | 0일 |
|
||||||
| Qwen3.6 Plus | 사용되지 않음 | 0일 |
|
| Qwen3.6 Plus | 사용되지 않음 | 0일 |
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ Den nåværende listen over modeller inkluderer:
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -107,8 +106,7 @@ Tabellen nedenfor gir et estimert antall forespørsler basert på typiske bruksm
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -126,7 +124,6 @@ Estimatene er basert på observerte forespørselsmønstre:
|
||||||
- DeepSeek V4 Flash — 790 input, 68 000 bufret, 280 output-tokens per forespørsel
|
- DeepSeek V4 Flash — 790 input, 68 000 bufret, 280 output-tokens per forespørsel
|
||||||
- MiniMax M3 — 510 input, 56 000 bufret, 190 output-tokens per forespørsel
|
- MiniMax M3 — 510 input, 56 000 bufret, 190 output-tokens per forespørsel
|
||||||
- MiniMax M2.7 — 300 input, 55 000 bufret, 125 output-tokens per forespørsel
|
- MiniMax M2.7 — 300 input, 55 000 bufret, 125 output-tokens per forespørsel
|
||||||
- Qwen3.8 Max — 420 input, 66 000 bufret, 200 output-tokens per forespørsel
|
|
||||||
- Qwen3.7 Max — 420 input, 66 000 bufret, 200 output-tokens per forespørsel
|
- Qwen3.7 Max — 420 input, 66 000 bufret, 200 output-tokens per forespørsel
|
||||||
- Qwen3.7 Plus — 500 input, 57 000 bufret, 190 output-tokens per forespørsel
|
- Qwen3.7 Plus — 500 input, 57 000 bufret, 190 output-tokens per forespørsel
|
||||||
- Qwen3.6 Plus — 500 input, 57 000 bufret, 190 output-tokens per forespørsel
|
- Qwen3.6 Plus — 500 input, 57 000 bufret, 190 output-tokens per forespørsel
|
||||||
|
|
@ -151,7 +148,6 @@ Estimatene er også basert på følgende priser per 1M tokens og den månedlige
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -211,7 +207,6 @@ Du kan også få tilgang til Go-modeller gjennom følgende API-endepunkter.
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -246,7 +241,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | Brukes ikke | 0 dager |
|
| Kimi K2.6 | Brukes ikke | 0 dager |
|
||||||
| MiMo-V2.5-Pro | Brukes ikke | 0 dager |
|
| MiMo-V2.5-Pro | Brukes ikke | 0 dager |
|
||||||
| MiMo-V2.5 | Brukes ikke | 0 dager |
|
| MiMo-V2.5 | Brukes ikke | 0 dager |
|
||||||
| Qwen3.8 Max | Brukes ikke | 0 dager |
|
|
||||||
| Qwen3.7 Max | Brukes ikke | 0 dager |
|
| Qwen3.7 Max | Brukes ikke | 0 dager |
|
||||||
| Qwen3.7 Plus | Brukes ikke | 0 dager |
|
| Qwen3.7 Plus | Brukes ikke | 0 dager |
|
||||||
| Qwen3.6 Plus | Brukes ikke | 0 dager |
|
| Qwen3.6 Plus | Brukes ikke | 0 dager |
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,6 @@ Obecna lista modeli obejmuje:
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -101,8 +100,7 @@ Poniższa tabela przedstawia szacunkową liczbę żądań na podstawie typowych
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -120,7 +118,6 @@ Szacunki te opierają się na zaobserwowanych wzorcach żądań:
|
||||||
- DeepSeek V4 Flash — 790 tokenów wejściowych, 68 000 w pamięci podręcznej, 280 tokenów wyjściowych na żądanie
|
- DeepSeek V4 Flash — 790 tokenów wejściowych, 68 000 w pamięci podręcznej, 280 tokenów wyjściowych na żądanie
|
||||||
- MiniMax M3 — 510 tokenów wejściowych, 56 000 w pamięci podręcznej, 190 tokenów wyjściowych na żądanie
|
- MiniMax M3 — 510 tokenów wejściowych, 56 000 w pamięci podręcznej, 190 tokenów wyjściowych na żądanie
|
||||||
- MiniMax M2.7 — 300 tokenów wejściowych, 55 000 w pamięci podręcznej, 125 tokenów wyjściowych na żądanie
|
- MiniMax M2.7 — 300 tokenów wejściowych, 55 000 w pamięci podręcznej, 125 tokenów wyjściowych na żądanie
|
||||||
- Qwen3.8 Max — 420 tokenów wejściowych, 66 000 w pamięci podręcznej, 200 tokenów wyjściowych na żądanie
|
|
||||||
- Qwen3.7 Max — 420 tokenów wejściowych, 66 000 w pamięci podręcznej, 200 tokenów wyjściowych na żądanie
|
- Qwen3.7 Max — 420 tokenów wejściowych, 66 000 w pamięci podręcznej, 200 tokenów wyjściowych na żądanie
|
||||||
- Qwen3.7 Plus — 500 tokenów wejściowych, 57 000 w pamięci podręcznej, 190 tokenów wyjściowych na żądanie
|
- Qwen3.7 Plus — 500 tokenów wejściowych, 57 000 w pamięci podręcznej, 190 tokenów wyjściowych na żądanie
|
||||||
- Qwen3.6 Plus — 500 tokenów wejściowych, 57 000 w pamięci podręcznej, 190 tokenów wyjściowych na żądanie
|
- Qwen3.6 Plus — 500 tokenów wejściowych, 57 000 w pamięci podręcznej, 190 tokenów wyjściowych na żądanie
|
||||||
|
|
@ -145,7 +142,6 @@ Szacunki opierają się również na następujących cenach za 1M tokenów oraz
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -203,7 +199,6 @@ Możesz również uzyskać dostęp do modeli Go za pośrednictwem następującyc
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -238,7 +233,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | Niewykorzystywane | 0 dni |
|
| Kimi K2.6 | Niewykorzystywane | 0 dni |
|
||||||
| MiMo-V2.5-Pro | Niewykorzystywane | 0 dni |
|
| MiMo-V2.5-Pro | Niewykorzystywane | 0 dni |
|
||||||
| MiMo-V2.5 | Niewykorzystywane | 0 dni |
|
| MiMo-V2.5 | Niewykorzystywane | 0 dni |
|
||||||
| Qwen3.8 Max | Niewykorzystywane | 0 dni |
|
|
||||||
| Qwen3.7 Max | Niewykorzystywane | 0 dni |
|
| Qwen3.7 Max | Niewykorzystywane | 0 dni |
|
||||||
| Qwen3.7 Plus | Niewykorzystywane | 0 dni |
|
| Qwen3.7 Plus | Niewykorzystywane | 0 dni |
|
||||||
| Qwen3.6 Plus | Niewykorzystywane | 0 dni |
|
| Qwen3.6 Plus | Niewykorzystywane | 0 dni |
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ A lista atual de modelos inclui:
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -107,8 +106,7 @@ A tabela abaixo fornece uma contagem estimada de requisições com base nos padr
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -126,7 +124,6 @@ As estimativas se baseiam nos padrões de requisições observados:
|
||||||
- DeepSeek V4 Flash — 790 tokens de entrada, 68.000 em cache, 280 tokens de saída por requisição
|
- DeepSeek V4 Flash — 790 tokens de entrada, 68.000 em cache, 280 tokens de saída por requisição
|
||||||
- MiniMax M3 — 510 tokens de entrada, 56.000 em cache, 190 tokens de saída por requisição
|
- MiniMax M3 — 510 tokens de entrada, 56.000 em cache, 190 tokens de saída por requisição
|
||||||
- MiniMax M2.7 — 300 tokens de entrada, 55.000 em cache, 125 tokens de saída por requisição
|
- MiniMax M2.7 — 300 tokens de entrada, 55.000 em cache, 125 tokens de saída por requisição
|
||||||
- Qwen3.8 Max — 420 tokens de entrada, 66.000 em cache, 200 tokens de saída por requisição
|
|
||||||
- Qwen3.7 Max — 420 tokens de entrada, 66.000 em cache, 200 tokens de saída por requisição
|
- Qwen3.7 Max — 420 tokens de entrada, 66.000 em cache, 200 tokens de saída por requisição
|
||||||
- Qwen3.7 Plus — 500 tokens de entrada, 57.000 em cache, 190 tokens de saída por requisição
|
- Qwen3.7 Plus — 500 tokens de entrada, 57.000 em cache, 190 tokens de saída por requisição
|
||||||
- Qwen3.6 Plus — 500 tokens de entrada, 57.000 em cache, 190 tokens de saída por requisição
|
- Qwen3.6 Plus — 500 tokens de entrada, 57.000 em cache, 190 tokens de saída por requisição
|
||||||
|
|
@ -151,7 +148,6 @@ As estimativas também se baseiam nos seguintes preços por 1M tokens e no uso m
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -211,7 +207,6 @@ Você também pode acessar os modelos do Go através dos seguintes endpoints de
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -246,7 +241,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | Não usado | 0 dias |
|
| Kimi K2.6 | Não usado | 0 dias |
|
||||||
| MiMo-V2.5-Pro | Não usado | 0 dias |
|
| MiMo-V2.5-Pro | Não usado | 0 dias |
|
||||||
| MiMo-V2.5 | Não usado | 0 dias |
|
| MiMo-V2.5 | Não usado | 0 dias |
|
||||||
| Qwen3.8 Max | Não usado | 0 dias |
|
|
||||||
| Qwen3.7 Max | Não usado | 0 dias |
|
| Qwen3.7 Max | Não usado | 0 dias |
|
||||||
| Qwen3.7 Plus | Não usado | 0 dias |
|
| Qwen3.7 Plus | Não usado | 0 dias |
|
||||||
| Qwen3.6 Plus | Não usado | 0 dias |
|
| Qwen3.6 Plus | Não usado | 0 dias |
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ OpenCode Go работает так же, как и любой другой пр
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -107,8 +106,7 @@ OpenCode Go включает следующие лимиты:
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -126,7 +124,6 @@ OpenCode Go включает следующие лимиты:
|
||||||
- DeepSeek V4 Flash — 790 входных, 68,000 кешированных, 280 выходных токенов на запрос
|
- DeepSeek V4 Flash — 790 входных, 68,000 кешированных, 280 выходных токенов на запрос
|
||||||
- MiniMax M3 — 510 входных, 56,000 кешированных, 190 выходных токенов на запрос
|
- MiniMax M3 — 510 входных, 56,000 кешированных, 190 выходных токенов на запрос
|
||||||
- MiniMax M2.7 — 300 входных, 55,000 кешированных, 125 выходных токенов на запрос
|
- MiniMax M2.7 — 300 входных, 55,000 кешированных, 125 выходных токенов на запрос
|
||||||
- Qwen3.8 Max — 420 входных, 66,000 кешированных, 200 выходных токенов на запрос
|
|
||||||
- Qwen3.7 Max — 420 входных, 66,000 кешированных, 200 выходных токенов на запрос
|
- Qwen3.7 Max — 420 входных, 66,000 кешированных, 200 выходных токенов на запрос
|
||||||
- Qwen3.7 Plus — 500 входных, 57,000 кешированных, 190 выходных токенов на запрос
|
- Qwen3.7 Plus — 500 входных, 57,000 кешированных, 190 выходных токенов на запрос
|
||||||
- Qwen3.6 Plus — 500 входных, 57,000 кешированных, 190 выходных токенов на запрос
|
- Qwen3.6 Plus — 500 входных, 57,000 кешированных, 190 выходных токенов на запрос
|
||||||
|
|
@ -151,7 +148,6 @@ OpenCode Go включает следующие лимиты:
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -211,7 +207,6 @@ OpenCode Go включает следующие лимиты:
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -246,7 +241,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | Не используется | 0 дней |
|
| Kimi K2.6 | Не используется | 0 дней |
|
||||||
| MiMo-V2.5-Pro | Не используется | 0 дней |
|
| MiMo-V2.5-Pro | Не используется | 0 дней |
|
||||||
| MiMo-V2.5 | Не используется | 0 дней |
|
| MiMo-V2.5 | Не используется | 0 дней |
|
||||||
| Qwen3.8 Max | Не используется | 0 дней |
|
|
||||||
| Qwen3.7 Max | Не используется | 0 дней |
|
| Qwen3.7 Max | Не используется | 0 дней |
|
||||||
| Qwen3.7 Plus | Не используется | 0 дней |
|
| Qwen3.7 Plus | Не используется | 0 дней |
|
||||||
| Qwen3.6 Plus | Не используется | 0 дней |
|
| Qwen3.6 Plus | Не используется | 0 дней |
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ OpenCode Go ทำงานเหมือนกับผู้ให้บร
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -97,8 +96,7 @@ OpenCode Go มีขีดจำกัดดังต่อไปนี้:
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -116,7 +114,6 @@ OpenCode Go มีขีดจำกัดดังต่อไปนี้:
|
||||||
- DeepSeek V4 Flash — 790 input, 68,000 cached, 280 output tokens ต่อ request
|
- DeepSeek V4 Flash — 790 input, 68,000 cached, 280 output tokens ต่อ request
|
||||||
- MiniMax M3 — 510 input, 56,000 cached, 190 output tokens ต่อ request
|
- MiniMax M3 — 510 input, 56,000 cached, 190 output tokens ต่อ request
|
||||||
- MiniMax M2.7 — 300 input, 55,000 cached, 125 output tokens ต่อ request
|
- MiniMax M2.7 — 300 input, 55,000 cached, 125 output tokens ต่อ request
|
||||||
- Qwen3.8 Max — 420 input, 66,000 cached, 200 output tokens ต่อ request
|
|
||||||
- Qwen3.7 Max — 420 input, 66,000 cached, 200 output tokens ต่อ request
|
- Qwen3.7 Max — 420 input, 66,000 cached, 200 output tokens ต่อ request
|
||||||
- Qwen3.7 Plus — 500 input, 57,000 cached, 190 output tokens ต่อ request
|
- Qwen3.7 Plus — 500 input, 57,000 cached, 190 output tokens ต่อ request
|
||||||
- Qwen3.6 Plus — 500 input, 57,000 cached, 190 output tokens ต่อ request
|
- Qwen3.6 Plus — 500 input, 57,000 cached, 190 output tokens ต่อ request
|
||||||
|
|
@ -141,7 +138,6 @@ OpenCode Go มีขีดจำกัดดังต่อไปนี้:
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -199,7 +195,6 @@ OpenCode Go มีขีดจำกัดดังต่อไปนี้:
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -232,7 +227,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | ไม่นำไปใช้ | 0 วัน |
|
| Kimi K2.6 | ไม่นำไปใช้ | 0 วัน |
|
||||||
| MiMo-V2.5-Pro | ไม่นำไปใช้ | 0 วัน |
|
| MiMo-V2.5-Pro | ไม่นำไปใช้ | 0 วัน |
|
||||||
| MiMo-V2.5 | ไม่นำไปใช้ | 0 วัน |
|
| MiMo-V2.5 | ไม่นำไปใช้ | 0 วัน |
|
||||||
| Qwen3.8 Max | ไม่นำไปใช้ | 0 วัน |
|
|
||||||
| Qwen3.7 Max | ไม่นำไปใช้ | 0 วัน |
|
| Qwen3.7 Max | ไม่นำไปใช้ | 0 วัน |
|
||||||
| Qwen3.7 Plus | ไม่นำไปใช้ | 0 วัน |
|
| Qwen3.7 Plus | ไม่นำไปใช้ | 0 วัน |
|
||||||
| Qwen3.6 Plus | ไม่นำไปใช้ | 0 วัน |
|
| Qwen3.6 Plus | ไม่นำไปใช้ | 0 วัน |
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ Mevcut model listesi şunları içerir:
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -97,8 +96,7 @@ Aşağıdaki tablo, tipik Go kullanım modellerine dayalı tahmini bir istek say
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -116,7 +114,6 @@ Tahminler, gözlemlenen istek modellerine dayanır:
|
||||||
- DeepSeek V4 Flash — İstek başına 790 girdi, 68.000 önbelleğe alınmış, 280 çıktı token'ı
|
- DeepSeek V4 Flash — İstek başına 790 girdi, 68.000 önbelleğe alınmış, 280 çıktı token'ı
|
||||||
- MiniMax M3 — İstek başına 510 girdi, 56.000 önbelleğe alınmış, 190 çıktı token'ı
|
- MiniMax M3 — İstek başına 510 girdi, 56.000 önbelleğe alınmış, 190 çıktı token'ı
|
||||||
- MiniMax M2.7 — İstek başına 300 girdi, 55.000 önbelleğe alınmış, 125 çıktı token'ı
|
- MiniMax M2.7 — İstek başına 300 girdi, 55.000 önbelleğe alınmış, 125 çıktı token'ı
|
||||||
- Qwen3.8 Max — İstek başına 420 girdi, 66.000 önbelleğe alınmış, 200 çıktı token'ı
|
|
||||||
- Qwen3.7 Max — İstek başına 420 girdi, 66.000 önbelleğe alınmış, 200 çıktı token'ı
|
- Qwen3.7 Max — İstek başına 420 girdi, 66.000 önbelleğe alınmış, 200 çıktı token'ı
|
||||||
- Qwen3.7 Plus — İstek başına 500 girdi, 57.000 önbelleğe alınmış, 190 çıktı token'ı
|
- Qwen3.7 Plus — İstek başına 500 girdi, 57.000 önbelleğe alınmış, 190 çıktı token'ı
|
||||||
- Qwen3.6 Plus — İstek başına 500 girdi, 57.000 önbelleğe alınmış, 190 çıktı token'ı
|
- Qwen3.6 Plus — İstek başına 500 girdi, 57.000 önbelleğe alınmış, 190 çıktı token'ı
|
||||||
|
|
@ -141,7 +138,6 @@ Tahminler ayrıca 1M token başına aşağıdaki fiyatlara ve her modelle birlik
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -199,7 +195,6 @@ Go modellerine aşağıdaki API uç noktaları aracılığıyla da erişebilirsi
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -232,7 +227,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | Kullanılmaz | 0 gün |
|
| Kimi K2.6 | Kullanılmaz | 0 gün |
|
||||||
| MiMo-V2.5-Pro | Kullanılmaz | 0 gün |
|
| MiMo-V2.5-Pro | Kullanılmaz | 0 gün |
|
||||||
| MiMo-V2.5 | Kullanılmaz | 0 gün |
|
| MiMo-V2.5 | Kullanılmaz | 0 gün |
|
||||||
| Qwen3.8 Max | Kullanılmaz | 0 gün |
|
|
||||||
| Qwen3.7 Max | Kullanılmaz | 0 gün |
|
| Qwen3.7 Max | Kullanılmaz | 0 gün |
|
||||||
| Qwen3.7 Plus | Kullanılmaz | 0 gün |
|
| Qwen3.7 Plus | Kullanılmaz | 0 gün |
|
||||||
| Qwen3.6 Plus | Kullanılmaz | 0 gün |
|
| Qwen3.6 Plus | Kullanılmaz | 0 gün |
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ OpenCode Go 的工作方式与 OpenCode 中的其他提供商一样。
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -97,8 +96,7 @@ OpenCode Go 包含以下限制:
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -118,7 +116,6 @@ OpenCode Go 包含以下限制:
|
||||||
- MiMo-V2.5-Pro — 每次请求 790 个输入 token,86,000 个缓存 token,305 个输出 token
|
- MiMo-V2.5-Pro — 每次请求 790 个输入 token,86,000 个缓存 token,305 个输出 token
|
||||||
- MiniMax M3 — 每次请求 510 个输入 token,56,000 个缓存 token,190 个输出 token
|
- MiniMax M3 — 每次请求 510 个输入 token,56,000 个缓存 token,190 个输出 token
|
||||||
- MiniMax M2.7 — 每次请求 300 个输入 token,55,000 个缓存 token,125 个输出 token
|
- MiniMax M2.7 — 每次请求 300 个输入 token,55,000 个缓存 token,125 个输出 token
|
||||||
- Qwen3.8 Max — 每次请求 420 个输入 token,66,000 个缓存 token,200 个输出 token
|
|
||||||
- Qwen3.7 Max — 每次请求 420 个输入 token,66,000 个缓存 token,200 个输出 token
|
- Qwen3.7 Max — 每次请求 420 个输入 token,66,000 个缓存 token,200 个输出 token
|
||||||
- Qwen3.7 Plus — 每次请求 500 个输入 token,57,000 个缓存 token,190 个输出 token
|
- Qwen3.7 Plus — 每次请求 500 个输入 token,57,000 个缓存 token,190 个输出 token
|
||||||
- Qwen3.6 Plus — 每次请求 500 个输入 token,57,000 个缓存 token,190 个输出 token
|
- Qwen3.6 Plus — 每次请求 500 个输入 token,57,000 个缓存 token,190 个输出 token
|
||||||
|
|
@ -141,7 +138,6 @@ OpenCode Go 包含以下限制:
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -199,7 +195,6 @@ OpenCode Go 包含以下限制:
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -232,7 +227,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | 不使用 | 0 天 |
|
| Kimi K2.6 | 不使用 | 0 天 |
|
||||||
| MiMo-V2.5-Pro | 不使用 | 0 天 |
|
| MiMo-V2.5-Pro | 不使用 | 0 天 |
|
||||||
| MiMo-V2.5 | 不使用 | 0 天 |
|
| MiMo-V2.5 | 不使用 | 0 天 |
|
||||||
| Qwen3.8 Max | 不使用 | 0 天 |
|
|
||||||
| Qwen3.7 Max | 不使用 | 0 天 |
|
| Qwen3.7 Max | 不使用 | 0 天 |
|
||||||
| Qwen3.7 Plus | 不使用 | 0 天 |
|
| Qwen3.7 Plus | 不使用 | 0 天 |
|
||||||
| Qwen3.6 Plus | 不使用 | 0 天 |
|
| Qwen3.6 Plus | 不使用 | 0 天 |
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ OpenCode Go 的運作方式與 OpenCode 中的任何其他供應商相同。
|
||||||
- **MiMo-V2.5-Pro**
|
- **MiMo-V2.5-Pro**
|
||||||
- **MiniMax M3**
|
- **MiniMax M3**
|
||||||
- **MiniMax M2.7**
|
- **MiniMax M2.7**
|
||||||
- **Qwen3.8 Max**
|
|
||||||
- **Qwen3.7 Max**
|
- **Qwen3.7 Max**
|
||||||
- **Qwen3.7 Plus**
|
- **Qwen3.7 Plus**
|
||||||
- **Qwen3.6 Plus**
|
- **Qwen3.6 Plus**
|
||||||
|
|
@ -97,8 +96,7 @@ OpenCode Go 包含以下限制:
|
||||||
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
| MiMo-V2.5-Pro | 3,250 | 8,150 | 16,300 |
|
||||||
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
| MiniMax M3 | 3,200 | 8,000 | 16,000 |
|
||||||
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
| MiniMax M2.7 | 3,400 | 8,500 | 17,000 |
|
||||||
| Qwen3.8 Max | 160 | 400 | 810 |
|
| Qwen3.7 Max | 950 | 2,390 | 4,770 |
|
||||||
| Qwen3.7 Max | 340 | 840 | 1,690 |
|
|
||||||
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
| Qwen3.7 Plus | 4,300 | 10,800 | 21,600 |
|
||||||
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
| Qwen3.6 Plus | 3,300 | 8,200 | 16,300 |
|
||||||
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
| DeepSeek V4 Pro | 3,450 | 8,550 | 17,150 |
|
||||||
|
|
@ -116,7 +114,6 @@ OpenCode Go 包含以下限制:
|
||||||
- DeepSeek V4 Flash — 每次請求 790 個輸入 token、68,000 個快取 token、280 個輸出 token
|
- DeepSeek V4 Flash — 每次請求 790 個輸入 token、68,000 個快取 token、280 個輸出 token
|
||||||
- MiniMax M3 — 每次請求 510 個輸入 token、56,000 個快取 token、190 個輸出 token
|
- MiniMax M3 — 每次請求 510 個輸入 token、56,000 個快取 token、190 個輸出 token
|
||||||
- MiniMax M2.7 — 每次請求 300 個輸入 token、55,000 個快取 token、125 個輸出 token
|
- MiniMax M2.7 — 每次請求 300 個輸入 token、55,000 個快取 token、125 個輸出 token
|
||||||
- Qwen3.8 Max — 每次請求 420 個輸入 token、66,000 個快取 token、200 個輸出 token
|
|
||||||
- Qwen3.7 Max — 每次請求 420 個輸入 token、66,000 個快取 token、200 個輸出 token
|
- Qwen3.7 Max — 每次請求 420 個輸入 token、66,000 個快取 token、200 個輸出 token
|
||||||
- Qwen3.7 Plus — 每次請求 500 個輸入 token、57,000 個快取 token、190 個輸出 token
|
- Qwen3.7 Plus — 每次請求 500 個輸入 token、57,000 個快取 token、190 個輸出 token
|
||||||
- Qwen3.6 Plus — 每次請求 500 個輸入 token、57,000 個快取 token、190 個輸出 token
|
- Qwen3.6 Plus — 每次請求 500 個輸入 token、57,000 個快取 token、190 個輸出 token
|
||||||
|
|
@ -141,7 +138,6 @@ OpenCode Go 包含以下限制:
|
||||||
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
| MiniMax M3 | $0.30 | $1.20 | $0.06 | - | $60 |
|
||||||
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.7 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
| MiniMax M2.5 | $0.30 | $1.20 | $0.06 | $0.375 | $60 |
|
||||||
| Qwen3.8 Max | $2.00 | $6.00 | $0.25 | $2.50 | $15 |
|
|
||||||
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
| Qwen3.7 Max | $2.50 | $7.50 | $0.50 | $3.125 | $60 |
|
||||||
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
| Qwen3.7 Plus (≤ 256K tokens) | $0.40 | $1.60 | $0.04 | $0.50 | $60 |
|
||||||
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
| Qwen3.7 Plus (> 256K tokens) | $1.20 | $4.80 | $0.12 | $1.50 | $60 |
|
||||||
|
|
@ -199,7 +195,6 @@ OpenCode Go 包含以下限制:
|
||||||
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M3 | minimax-m3 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.7 | minimax-m2.7 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| MiniMax M2.5 | minimax-m2.5 | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.8 Max | qwen3.8-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
|
||||||
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Max | qwen3.7-max | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.7 Plus | qwen3.7-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
| Qwen3.6 Plus | qwen3.6-plus | `https://opencode.ai/zen/go/v1/messages` | `@ai-sdk/anthropic` |
|
||||||
|
|
@ -232,7 +227,6 @@ https://opencode.ai/zen/go/v1/models
|
||||||
| Kimi K2.6 | 不使用 | 0 天 |
|
| Kimi K2.6 | 不使用 | 0 天 |
|
||||||
| MiMo-V2.5-Pro | 不使用 | 0 天 |
|
| MiMo-V2.5-Pro | 不使用 | 0 天 |
|
||||||
| MiMo-V2.5 | 不使用 | 0 天 |
|
| MiMo-V2.5 | 不使用 | 0 天 |
|
||||||
| Qwen3.8 Max | 不使用 | 0 天 |
|
|
||||||
| Qwen3.7 Max | 不使用 | 0 天 |
|
| Qwen3.7 Max | 不使用 | 0 天 |
|
||||||
| Qwen3.7 Plus | 不使用 | 0 天 |
|
| Qwen3.7 Plus | 不使用 | 0 天 |
|
||||||
| Qwen3.6 Plus | 不使用 | 0 天 |
|
| Qwen3.6 Plus | 不使用 | 0 天 |
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue