mirror of
https://github.com/aljazceru/opencode.git
synced 2025-12-24 11:14:23 +01:00
big format
This commit is contained in:
@@ -77,4 +77,4 @@
|
||||
background-color: var(--color-accent-alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,10 @@ export function Faq(props: ParentProps & { question: string }) {
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M12.5 11.5H19V12.5H12.5V19H11.5V12.5H5V11.5H11.5V5H12.5V11.5Z" fill="currentColor" />
|
||||
<path
|
||||
d="M12.5 11.5H19V12.5H12.5V19H11.5V12.5H5V11.5H11.5V5H12.5V11.5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
data-slot="faq-icon-minus"
|
||||
|
||||
@@ -9,10 +9,23 @@ export function IconLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
<path d="M13.7124 9.14333V4.5719H18.2838V9.14333H13.7124Z" fill="currentColor" />
|
||||
<path d="M13.7124 13.7136V9.14221H18.2838V13.7136H13.7124Z" fill="currentColor" />
|
||||
<path d="M0 18.2857V13.7142H4.57143V18.2857H0Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<rect width="4.57143" height="4.57143" transform="translate(4.57178 13.7141)" fill="currentColor" />
|
||||
<path d="M4.57178 18.2855V13.7141H9.14321V18.2855H4.57178Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<rect
|
||||
width="4.57143"
|
||||
height="4.57143"
|
||||
transform="translate(4.57178 13.7141)"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M4.57178 18.2855V13.7141H9.14321V18.2855H4.57178Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path d="M9.1438 18.2855V13.7141H13.7152V18.2855H9.1438Z" fill="currentColor" />
|
||||
<path d="M13.7156 18.2855V13.7141H18.287V18.2855H13.7156Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<path
|
||||
d="M13.7156 18.2855V13.7141H18.287V18.2855H13.7156Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<rect width="4.57143" height="4.57143" transform="translate(0 18.2859)" fill="currentColor" />
|
||||
<path d="M0 22.8572V18.2858H4.57143V22.8572H0Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<rect
|
||||
@@ -23,8 +36,16 @@ export function IconLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path d="M4.57178 22.8573V18.2859H9.14321V22.8573H4.57178Z" fill="currentColor" />
|
||||
<path d="M9.1438 22.8573V18.2859H13.7152V22.8573H9.1438Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<path d="M13.7156 22.8573V18.2859H18.287V22.8573H13.7156Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<path
|
||||
d="M9.1438 22.8573V18.2859H13.7152V22.8573H9.1438Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path
|
||||
d="M13.7156 22.8573V18.2859H18.287V22.8573H13.7156Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path d="M0 27.4292V22.8578H4.57143V27.4292H0Z" fill="currentColor" />
|
||||
<path d="M4.57178 27.4292V22.8578H9.14321V27.4292H4.57178Z" fill="currentColor" />
|
||||
<path d="M9.1438 27.4276V22.8562H13.7152V27.4276H9.1438Z" fill="currentColor" />
|
||||
@@ -40,9 +61,21 @@ export function IconLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
<path d="M32.001 18.2855V13.7141H36.5724V18.2855H32.001Z" fill="currentColor" />
|
||||
<path d="M36.5698 18.2855V13.7141H41.1413V18.2855H36.5698Z" fill="currentColor" />
|
||||
<path d="M22.8572 22.8573V18.2859H27.4286V22.8573H22.8572Z" fill="currentColor" />
|
||||
<path d="M27.4292 22.8573V18.2859H32.0006V22.8573H27.4292Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<path d="M32.001 22.8573V18.2859H36.5724V22.8573H32.001Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<path d="M36.5698 22.8573V18.2859H41.1413V22.8573H36.5698Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<path
|
||||
d="M27.4292 22.8573V18.2859H32.0006V22.8573H27.4292Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path
|
||||
d="M32.001 22.8573V18.2859H36.5724V22.8573H32.001Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path
|
||||
d="M36.5698 22.8573V18.2859H41.1413V22.8573H36.5698Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path d="M22.8572 27.4292V22.8578H27.4286V27.4292H22.8572Z" fill="currentColor" />
|
||||
<path d="M27.4292 27.4276V22.8562H32.0006V27.4276H27.4292Z" fill="currentColor" />
|
||||
<path d="M32.001 27.4276V22.8562H36.5724V27.4276H32.001Z" fill="currentColor" />
|
||||
@@ -53,16 +86,40 @@ export function IconLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
<path d="M45.7144 13.7136V9.14221H50.2858V13.7136H45.7144Z" fill="currentColor" />
|
||||
<path d="M59.4299 13.7152V9.1438H64.0014V13.7152H59.4299Z" fill="currentColor" />
|
||||
<path d="M45.7144 18.2855V13.7141H50.2858V18.2855H45.7144Z" fill="currentColor" />
|
||||
<path d="M50.2861 18.2857V13.7142H54.8576V18.2857H50.2861Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<path d="M54.8579 18.2855V13.7141H59.4293V18.2855H54.8579Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<path
|
||||
d="M50.2861 18.2857V13.7142H54.8576V18.2857H50.2861Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path
|
||||
d="M54.8579 18.2855V13.7141H59.4293V18.2855H54.8579Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path d="M59.4299 18.2855V13.7141H64.0014V18.2855H59.4299Z" fill="currentColor" />
|
||||
<path d="M45.7144 22.8573V18.2859H50.2858V22.8573H45.7144Z" fill="currentColor" />
|
||||
<path d="M50.2861 22.8572V18.2858H54.8576V22.8572H50.2861Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<path d="M54.8579 22.8573V18.2859H59.4293V22.8573H54.8579Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<path
|
||||
d="M50.2861 22.8572V18.2858H54.8576V22.8572H50.2861Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path
|
||||
d="M54.8579 22.8573V18.2859H59.4293V22.8573H54.8579Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path d="M59.4299 22.8573V18.2859H64.0014V22.8573H59.4299Z" fill="currentColor" />
|
||||
<path d="M45.7144 27.4292V22.8578H50.2858V27.4292H45.7144Z" fill="currentColor" />
|
||||
<path d="M50.2861 27.4286V22.8572H54.8576V27.4286H50.2861Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<path d="M54.8579 27.4285V22.8571H59.4293V27.4285H54.8579Z" fill="currentColor" fill-opacity="0.2" />
|
||||
<path
|
||||
d="M50.2861 27.4286V22.8572H54.8576V27.4286H50.2861Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path
|
||||
d="M54.8579 27.4285V22.8571H59.4293V27.4285H54.8579Z"
|
||||
fill="currentColor"
|
||||
fill-opacity="0.2"
|
||||
/>
|
||||
<path d="M59.4299 27.4292V22.8578H64.0014V27.4292H59.4299Z" fill="currentColor" />
|
||||
</svg>
|
||||
)
|
||||
@@ -70,7 +127,14 @@ export function IconLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
|
||||
export function IconCopy(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
{...props}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M8.75 8.75V2.75H21.25V15.25H15.25M15.25 8.75H2.75V21.25H15.25V8.75Z"
|
||||
stroke="currentColor"
|
||||
@@ -83,8 +147,20 @@ export function IconCopy(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
|
||||
export function IconCheck(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.75 15.0938L9 20.25L21.25 3.75" stroke="#03B000" stroke-width="2" stroke-linecap="square" />
|
||||
<svg
|
||||
{...props}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M2.75 15.0938L9 20.25L21.25 3.75"
|
||||
stroke="#03B000"
|
||||
stroke-width="2"
|
||||
stroke-linecap="square"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -113,7 +189,14 @@ export function IconStripe(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
|
||||
export function IconChevron(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} width="8" height="6" viewBox="0 0 8 6" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
{...props}
|
||||
width="8"
|
||||
height="6"
|
||||
viewBox="0 0 8 6"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M4.00024 5.04041L7.37401 1.66663L6.66691 0.959525L4.00024 3.62619L1.33357 0.959525L0.626465 1.66663L4.00024 5.04041Z"
|
||||
@@ -124,7 +207,14 @@ export function IconChevron(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
|
||||
export function IconWorkspaceLogo(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} width="24" height="30" viewBox="0 0 24 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
{...props}
|
||||
width="24"
|
||||
height="30"
|
||||
viewBox="0 0 24 30"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M18 6H6V24H18V6ZM24 30H0V0H24V30Z" fill="currentColor" />
|
||||
</svg>
|
||||
)
|
||||
@@ -144,7 +234,10 @@ export function IconOpenAI(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
export function IconAnthropic(props: JSX.SvgSVGAttributes<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor" d="M13.7891 3.93188L20.2223 20.068H23.7502L17.317 3.93188H13.7891Z" />
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M13.7891 3.93188L20.2223 20.068H23.7502L17.317 3.93188H13.7891Z"
|
||||
/>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M6.32538 13.6827L8.52662 8.01201L10.7279 13.6827H6.32538ZM6.68225 3.93188L0.25 20.068H3.84652L5.16202 16.6794H11.8914L13.2067 20.068H16.8033L10.371 3.93188H6.68225Z"
|
||||
|
||||
@@ -63,4 +63,4 @@
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ import type { APIEvent } from "@solidjs/start/server"
|
||||
import { AuthClient } from "~/context/auth"
|
||||
|
||||
export async function GET(input: APIEvent) {
|
||||
const result = await AuthClient.authorize(new URL("./callback", input.request.url).toString(), "code")
|
||||
const result = await AuthClient.authorize(
|
||||
new URL("./callback", input.request.url).toString(),
|
||||
"code",
|
||||
)
|
||||
return Response.redirect(result.url, 302)
|
||||
}
|
||||
|
||||
@@ -299,7 +299,6 @@
|
||||
transition: all 0.2s ease;
|
||||
text-decoration: none;
|
||||
|
||||
|
||||
&:hover:not(:disabled) {
|
||||
background: var(--color-background-strong-hover);
|
||||
}
|
||||
@@ -385,23 +384,21 @@
|
||||
0 1px 2px -1px rgba(19, 16, 16, 0.12);
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(19, 16, 16, 0.16)
|
||||
box-shadow: 0 0 0 1px rgba(19, 16, 16, 0.16);
|
||||
}
|
||||
|
||||
|
||||
&:hover {
|
||||
background: var(--color-background);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
box-shadow: 0 0 0 1px rgba(19, 16, 16, 0.08), 0 6px 8px -8px rgba(19, 16, 16, 0.50);
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(19, 16, 16, 0.08),
|
||||
0 6px 8px -8px rgba(19, 16, 16, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@media (max-width: 60rem) {
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
color: var(--color-danger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,4 +71,4 @@
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,10 @@ export function WorkspacePicker() {
|
||||
<Dropdown trigger={currentWorkspace()} align="left">
|
||||
<For each={workspaces()}>
|
||||
{(workspace) => (
|
||||
<DropdownItem selected={workspace.id === params.id} onClick={() => handleSelectWorkspace(workspace.id)}>
|
||||
<DropdownItem
|
||||
selected={workspace.id === params.id}
|
||||
onClick={() => handleSelectWorkspace(workspace.id)}
|
||||
>
|
||||
{workspace.name || workspace.slug}
|
||||
</DropdownItem>
|
||||
)}
|
||||
@@ -95,7 +98,11 @@ export function WorkspacePicker() {
|
||||
</button>
|
||||
</Dropdown>
|
||||
|
||||
<Modal open={store.showForm} onClose={() => setStore("showForm", false)} title="Create New Workspace">
|
||||
<Modal
|
||||
open={store.showForm}
|
||||
onClose={() => setStore("showForm", false)}
|
||||
title="Create New Workspace"
|
||||
>
|
||||
<form data-slot="create-form" action={createWorkspace} method="post">
|
||||
<div data-slot="create-input-group">
|
||||
<input
|
||||
|
||||
@@ -104,4 +104,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,4 +182,4 @@
|
||||
padding: var(--space-4);
|
||||
min-width: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,4 +93,4 @@
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,10 @@ export function PaymentSection() {
|
||||
<td data-slot="payment-receipt">
|
||||
<button
|
||||
onClick={async () => {
|
||||
const receiptUrl = await downloadReceiptAction(params.id, payment.paymentID!)
|
||||
const receiptUrl = await downloadReceiptAction(
|
||||
params.id,
|
||||
payment.paymentID!,
|
||||
)
|
||||
if (receiptUrl) {
|
||||
window.open(receiptUrl, "_blank")
|
||||
}
|
||||
|
||||
@@ -259,4 +259,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -171,7 +171,6 @@
|
||||
}
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: var(--space-2) var(--space-3);
|
||||
@@ -181,8 +180,7 @@
|
||||
th {
|
||||
&:nth-child(3)
|
||||
|
||||
/* Date */
|
||||
{
|
||||
/* Date */ {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -190,11 +188,10 @@
|
||||
td {
|
||||
&:nth-child(3)
|
||||
|
||||
/* Date */
|
||||
{
|
||||
/* Date */ {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,14 +146,20 @@ export function KeySection() {
|
||||
title="Copy API key"
|
||||
>
|
||||
<span>{key.keyDisplay}</span>
|
||||
<Show when={copied()} fallback={<IconCopy style={{ width: "14px", height: "14px" }} />}>
|
||||
<Show
|
||||
when={copied()}
|
||||
fallback={<IconCopy style={{ width: "14px", height: "14px" }} />}
|
||||
>
|
||||
<IconCheck style={{ width: "14px", height: "14px" }} />
|
||||
</Show>
|
||||
</button>
|
||||
</Show>
|
||||
</td>
|
||||
<td data-slot="key-user-email">{key.email}</td>
|
||||
<td data-slot="key-last-used" title={key.timeUsed ? formatDateUTC(key.timeUsed) : undefined}>
|
||||
<td
|
||||
data-slot="key-last-used"
|
||||
title={key.timeUsed ? formatDateUTC(key.timeUsed) : undefined}
|
||||
>
|
||||
{key.timeUsed ? formatDateForTable(key.timeUsed) : "-"}
|
||||
</td>
|
||||
<td data-slot="key-actions">
|
||||
|
||||
@@ -85,7 +85,12 @@ const updateMember = action(async (form: FormData) => {
|
||||
)
|
||||
}, "member.update")
|
||||
|
||||
function MemberRow(props: { member: any; workspaceID: string; actorID: string; actorRole: string }) {
|
||||
function MemberRow(props: {
|
||||
member: any
|
||||
workspaceID: string
|
||||
actorID: string
|
||||
actorRole: string
|
||||
}) {
|
||||
const submission = useSubmission(updateMember)
|
||||
const isCurrentUser = () => props.actorID === props.member.id
|
||||
const isAdmin = () => props.actorRole === "admin"
|
||||
|
||||
@@ -69,4 +69,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,4 +140,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,15 +43,24 @@ export function NewUserSection() {
|
||||
<div data-component="feature-grid">
|
||||
<div data-slot="feature">
|
||||
<h3>Tested & Verified Models</h3>
|
||||
<p>We've benchmarked and tested models specifically for coding agents to ensure the best performance.</p>
|
||||
<p>
|
||||
We've benchmarked and tested models specifically for coding agents to ensure the best
|
||||
performance.
|
||||
</p>
|
||||
</div>
|
||||
<div data-slot="feature">
|
||||
<h3>Highest Quality</h3>
|
||||
<p>Access models configured for optimal performance - no downgrades or routing to cheaper providers.</p>
|
||||
<p>
|
||||
Access models configured for optimal performance - no downgrades or routing to cheaper
|
||||
providers.
|
||||
</p>
|
||||
</div>
|
||||
<div data-slot="feature">
|
||||
<h3>No Lock-in</h3>
|
||||
<p>Use Zen with any coding agent, and continue using other providers with opencode whenever you want.</p>
|
||||
<p>
|
||||
Use Zen with any coding agent, and continue using other providers with opencode
|
||||
whenever you want.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -128,7 +128,6 @@
|
||||
}
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
|
||||
th,
|
||||
td {
|
||||
padding: var(--space-2) var(--space-3);
|
||||
@@ -136,4 +135,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,9 @@ const removeProvider = action(async (form: FormData) => {
|
||||
if (!provider) return { error: "Provider is required" }
|
||||
const workspaceID = form.get("workspaceID")?.toString()
|
||||
if (!workspaceID) return { error: "Workspace ID is required" }
|
||||
return json(await withActor(() => Provider.remove({ provider }), workspaceID), { revalidate: listProviders.key })
|
||||
return json(await withActor(() => Provider.remove({ provider }), workspaceID), {
|
||||
revalidate: listProviders.key,
|
||||
})
|
||||
}, "provider.remove")
|
||||
|
||||
const saveProvider = action(async (form: FormData) => {
|
||||
@@ -53,7 +55,10 @@ const listProviders = query(async (workspaceID: string) => {
|
||||
function ProviderRow(props: { provider: Provider }) {
|
||||
const params = useParams()
|
||||
const providers = createAsync(() => listProviders(params.id))
|
||||
const saveSubmission = useSubmission(saveProvider, ([fd]) => fd.get("provider")?.toString() === props.provider.key)
|
||||
const saveSubmission = useSubmission(
|
||||
saveProvider,
|
||||
([fd]) => fd.get("provider")?.toString() === props.provider.key,
|
||||
)
|
||||
const removeSubmission = useSubmission(
|
||||
removeProvider,
|
||||
([fd]) => fd.get("provider")?.toString() === props.provider.key,
|
||||
@@ -89,9 +94,16 @@ function ProviderRow(props: { provider: Provider }) {
|
||||
<td data-slot="provider-key">
|
||||
<Show
|
||||
when={store.editing}
|
||||
fallback={<span>{providerData() ? maskCredentials(providerData()!.credentials) : "-"}</span>}
|
||||
fallback={
|
||||
<span>{providerData() ? maskCredentials(providerData()!.credentials) : "-"}</span>
|
||||
}
|
||||
>
|
||||
<form id={`provider-form-${props.provider.key}`} action={saveProvider} method="post" data-slot="edit-form">
|
||||
<form
|
||||
id={`provider-form-${props.provider.key}`}
|
||||
action={saveProvider}
|
||||
method="post"
|
||||
data-slot="edit-form"
|
||||
>
|
||||
<div data-slot="input-wrapper">
|
||||
<input
|
||||
ref={(r) => (input = r)}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
>button {
|
||||
> button {
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
>button[type="reset"] {
|
||||
> button[type="reset"] {
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
@@ -91,4 +91,4 @@
|
||||
margin-top: calc(var(--space-1) * -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,10 @@ export default function Home() {
|
||||
createAsync(() => checkLoggedIn())
|
||||
return (
|
||||
<main data-page="zen">
|
||||
<HttpHeader name="Cache-Control" value="public, max-age=1, s-maxage=3600, stale-while-revalidate=86400" />
|
||||
<HttpHeader
|
||||
name="Cache-Control"
|
||||
value="public, max-age=1, s-maxage=3600, stale-while-revalidate=86400"
|
||||
/>
|
||||
<Title>OpenCode Zen | A curated set of reliable optimized models for coding agents</Title>
|
||||
<Link rel="icon" type="image/svg+xml" href="/favicon-zen.svg" />
|
||||
<Meta property="og:image" content="/social-share-zen.png" />
|
||||
@@ -44,13 +47,19 @@ export default function Home() {
|
||||
<img data-slot="zen logo dark" src={zenLogoDark} alt="zen logo dark" />
|
||||
<strong>Reliable optimized models for coding agents</strong>
|
||||
<p>
|
||||
Zen gives you access to a curated set of AI models that OpenCode has tested and benchmarked specifically
|
||||
for coding agents. No need to worry about inconsistent performance and quality, use validated models
|
||||
that work.
|
||||
Zen gives you access to a curated set of AI models that OpenCode has tested and
|
||||
benchmarked specifically for coding agents. No need to worry about inconsistent
|
||||
performance and quality, use validated models that work.
|
||||
</p>
|
||||
<div data-slot="model-logos">
|
||||
<div>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<mask
|
||||
id="mask0_79_128586"
|
||||
style="mask-type:luminance"
|
||||
@@ -71,8 +80,17 @@ export default function Home() {
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.7891 3.93164L20.2223 20.0677H23.7502L17.317 3.93164H13.7891Z" fill="currentColor" />
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M13.7891 3.93164L20.2223 20.0677H23.7502L17.317 3.93164H13.7891Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M6.32538 13.6824L8.52662 8.01177L10.7279 13.6824H6.32538ZM6.68225 3.93164L0.25 20.0677H3.84652L5.16202 16.6791H11.8914L13.2067 20.0677H16.8033L10.371 3.93164H6.68225Z"
|
||||
fill="currentColor"
|
||||
@@ -80,7 +98,13 @@ export default function Home() {
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.16861 16.0529L17.2018 9.85156C17.5957 9.54755 18.1586 9.66612 18.3463 10.1384C19.3339 12.6288 18.8926 15.6217 16.9276 17.6766C14.9626 19.7314 12.2285 20.1821 9.72948 19.1557L6.9995 20.4775C10.9151 23.2763 15.6699 22.5841 18.6411 19.4749C20.9979 17.0103 21.7278 13.6508 21.0453 10.6214L21.0515 10.6278C20.0617 6.17736 21.2948 4.39847 23.8207 0.760904C23.8804 0.674655 23.9402 0.588405 24 0.5L20.6762 3.97585V3.96506L9.16658 16.0551"
|
||||
fill="currentColor"
|
||||
@@ -92,7 +116,13 @@ export default function Home() {
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
@@ -102,7 +132,13 @@ export default function Home() {
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12.6241 11.346L20.3848 3.44816C20.5309 3.29931 20.4487 3 20.2601 3H16.0842C16.0388 3 15.9949 3.01897 15.9594 3.05541L7.59764 11.5629C7.46721 11.6944 7.27446 11.5771 7.27446 11.3666V3.25183C7.27446 3.11242 7.18515 3 7.07594 3H4.19843C4.08932 3 4 3.11242 4 3.25183V20.7482C4 20.8876 4.08932 21 4.19843 21H7.07594C7.18515 21 7.27446 20.8876 7.27446 20.7482V17.1834C7.27446 17.1073 7.30136 17.0344 7.34815 16.987L9.94075 14.3486C10.0031 14.2853 10.0895 14.2757 10.159 14.3232L17.0934 19.5573C18.2289 20.3412 19.4975 20.8226 20.786 20.9652C20.9008 20.9778 21 20.8606 21 20.7133V17.3559C21 17.2276 20.9249 17.1232 20.8243 17.1073C20.0659 16.9853 19.326 16.6845 18.6569 16.222L12.6538 11.764C12.5291 11.6785 12.5135 11.4584 12.6241 11.346Z"
|
||||
fill="currentColor"
|
||||
@@ -112,7 +148,13 @@ export default function Home() {
|
||||
</div>
|
||||
<a href="/auth">
|
||||
<span>Get started with Zen </span>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6.5 12L17 12M13 16.5L17.5 12L13 7.5"
|
||||
stroke="currentColor"
|
||||
@@ -124,14 +166,23 @@ export default function Home() {
|
||||
</div>
|
||||
<div data-slot="pricing-copy">
|
||||
<p>
|
||||
<strong>Add $20 Pay as you go balance</strong> <span>(+$1.23 card processing fee)</span>
|
||||
<strong>Add $20 Pay as you go balance</strong>{" "}
|
||||
<span>(+$1.23 card processing fee)</span>
|
||||
</p>
|
||||
<p>Use with any agent. Set monthly spend limits. Cancel any time.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section data-component="comparison">
|
||||
<video src={compareVideo} autoplay playsinline loop muted preload="auto" poster={compareVideoPoster}>
|
||||
<video
|
||||
src={compareVideo}
|
||||
autoplay
|
||||
playsinline
|
||||
loop
|
||||
muted
|
||||
preload="auto"
|
||||
poster={compareVideoPoster}
|
||||
>
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</section>
|
||||
@@ -140,8 +191,8 @@ export default function Home() {
|
||||
<div data-slot="section-title">
|
||||
<h3>What problem is Zen solving?</h3>
|
||||
<p>
|
||||
There are so many models available, but only a few work well with coding agents. Most providers
|
||||
configure them differently with varying results.
|
||||
There are so many models available, but only a few work well with coding agents.
|
||||
Most providers configure them differently with varying results.
|
||||
</p>
|
||||
</div>
|
||||
<p>We're fixing this for everyone, not just OpenCode users.</p>
|
||||
@@ -176,14 +227,15 @@ export default function Home() {
|
||||
<li>
|
||||
<span>[2]</span>
|
||||
<div>
|
||||
<strong>Use Zen with transparent pricing</strong> - <a href="/docs/zen/#pricing">pay per request</a>{" "}
|
||||
with zero markups
|
||||
<strong>Use Zen with transparent pricing</strong> -{" "}
|
||||
<a href="/docs/zen/#pricing">pay per request</a> with zero markups
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<span>[3]</span>
|
||||
<div>
|
||||
<strong>Auto-top up</strong> - when your balance reaches $5 we’ll automatically add $20
|
||||
<strong>Auto-top up</strong> - when your balance reaches $5 we’ll automatically
|
||||
add $20
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -195,8 +247,9 @@ export default function Home() {
|
||||
<div>
|
||||
<span>[*]</span>
|
||||
<p>
|
||||
All Zen models are hosted in the US. Providers follow a zero-retention policy and do not use your data
|
||||
for model training, with the <a href="/docs/zen/#privacy">following exceptions</a>.
|
||||
All Zen models are hosted in the US. Providers follow a zero-retention policy and
|
||||
do not use your data for model training, with the{" "}
|
||||
<a href="/docs/zen/#privacy">following exceptions</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -251,7 +304,8 @@ export default function Home() {
|
||||
<span>ex-Head of Design, Laravel</span>
|
||||
</div>
|
||||
<div data-slot="quote">
|
||||
With <span>@OpenCode</span> Zen I know all the models are tested and perfect for coding agents.
|
||||
With <span>@OpenCode</span> Zen I know all the models are tested and perfect for
|
||||
coding agents.
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@@ -275,38 +329,44 @@ export default function Home() {
|
||||
<ul>
|
||||
<li>
|
||||
<Faq question="What is OpenCode Zen?">
|
||||
Zen is a curated set of AI models tested and benchmarked for coding agents created by the team behind
|
||||
OpenCode.
|
||||
Zen is a curated set of AI models tested and benchmarked for coding agents created
|
||||
by the team behind OpenCode.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="What makes Zen more accurate?">
|
||||
Zen only provides models that have been specifically tested and benchmarked for coding agents. You
|
||||
wouldn’t use a butter knife to cut steak, don’t use poor models for coding.
|
||||
Zen only provides models that have been specifically tested and benchmarked for
|
||||
coding agents. You wouldn’t use a butter knife to cut steak, don’t use poor models
|
||||
for coding.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="Is Zen cheaper?">
|
||||
Zen is not for profit. Zen passes through the costs from the model providers to you. The higher Zen’s
|
||||
usage the more OpenCode can negotiate better rates and pass those to you.
|
||||
Zen is not for profit. Zen passes through the costs from the model providers to
|
||||
you. The higher Zen’s usage the more OpenCode can negotiate better rates and pass
|
||||
those to you.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="How much does Zen cost?">
|
||||
Zen <a href="/docs/zen/#pricing">charges per request</a> with zero markups, so you pay exactly what
|
||||
the model provider charges. Your total cost depends on usage, and you can set monthly spend limits in
|
||||
your <a href="/auth">account</a>. To cover costs, OpenCode adds only a small payment processing fee of
|
||||
$1.23 per $20 balance top-up.
|
||||
Zen <a href="/docs/zen/#pricing">charges per request</a> with zero markups, so you
|
||||
pay exactly what the model provider charges. Your total cost depends on usage, and
|
||||
you can set monthly spend limits in your <a href="/auth">account</a>. To cover
|
||||
costs, OpenCode adds only a small payment processing fee of $1.23 per $20 balance
|
||||
top-up.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="What about data and privacy?">
|
||||
All Zen models are hosted in the US. Providers follow a zero-retention policy and do not use your data
|
||||
for model training, with the <a href="/docs/zen/#privacy">following exceptions</a>.
|
||||
All Zen models are hosted in the US. Providers follow a zero-retention policy and
|
||||
do not use your data for model training, with the{" "}
|
||||
<a href="/docs/zen/#privacy">following exceptions</a>.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="Can I set spend limits?">Yes, you can set monthly spending limits in your account.</Faq>
|
||||
<Faq question="Can I set spend limits?">
|
||||
Yes, you can set monthly spending limits in your account.
|
||||
</Faq>
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="Can I cancel?">
|
||||
@@ -315,8 +375,8 @@ export default function Home() {
|
||||
</li>
|
||||
<li>
|
||||
<Faq question="Can I use Zen with other coding agents?">
|
||||
While Zen works great with OpenCode, you can use Zen with any agent. Follow the setup instructions in
|
||||
your preferred coding agent.
|
||||
While Zen works great with OpenCode, you can use Zen with any agent. Follow the
|
||||
setup instructions in your preferred coding agent.
|
||||
</Faq>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -98,7 +98,10 @@ export function fromAnthropicRequest(body: any): CommonRequest {
|
||||
typeof (src as any).media_type === "string" &&
|
||||
typeof (src as any).data === "string"
|
||||
)
|
||||
return { type: "image_url", image_url: { url: `data:${(src as any).media_type};base64,${(src as any).data}` } }
|
||||
return {
|
||||
type: "image_url",
|
||||
image_url: { url: `data:${(src as any).media_type};base64,${(src as any).data}` },
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -120,12 +123,15 @@ export function fromAnthropicRequest(body: any): CommonRequest {
|
||||
if ((p as any).type === "tool_result") {
|
||||
const id = (p as any).tool_use_id
|
||||
const content =
|
||||
typeof (p as any).content === "string" ? (p as any).content : JSON.stringify((p as any).content)
|
||||
typeof (p as any).content === "string"
|
||||
? (p as any).content
|
||||
: JSON.stringify((p as any).content)
|
||||
msgs.push({ role: "tool", tool_call_id: id, content })
|
||||
}
|
||||
}
|
||||
if (partsOut.length > 0) {
|
||||
if (partsOut.length === 1 && partsOut[0].type === "text") msgs.push({ role: "user", content: partsOut[0].text })
|
||||
if (partsOut.length === 1 && partsOut[0].type === "text")
|
||||
msgs.push({ role: "user", content: partsOut[0].text })
|
||||
else msgs.push({ role: "user", content: partsOut })
|
||||
}
|
||||
continue
|
||||
@@ -137,7 +143,8 @@ export function fromAnthropicRequest(body: any): CommonRequest {
|
||||
const tcs: any[] = []
|
||||
for (const p of partsIn) {
|
||||
if (!p || !(p as any).type) continue
|
||||
if ((p as any).type === "text" && typeof (p as any).text === "string") texts.push((p as any).text)
|
||||
if ((p as any).type === "text" && typeof (p as any).text === "string")
|
||||
texts.push((p as any).text)
|
||||
if ((p as any).type === "tool_use") {
|
||||
const name = (p as any).name
|
||||
const id = (p as any).id
|
||||
@@ -165,7 +172,11 @@ export function fromAnthropicRequest(body: any): CommonRequest {
|
||||
.filter((t: any) => t && typeof t === "object" && "input_schema" in t)
|
||||
.map((t: any) => ({
|
||||
type: "function",
|
||||
function: { name: (t as any).name, description: (t as any).description, parameters: (t as any).input_schema },
|
||||
function: {
|
||||
name: (t as any).name,
|
||||
description: (t as any).description,
|
||||
parameters: (t as any).input_schema,
|
||||
},
|
||||
}))
|
||||
: undefined
|
||||
|
||||
@@ -203,7 +214,9 @@ export function fromAnthropicRequest(body: any): CommonRequest {
|
||||
export function toAnthropicRequest(body: CommonRequest) {
|
||||
if (!body || typeof body !== "object") return body
|
||||
|
||||
const sysIn = Array.isArray(body.messages) ? body.messages.filter((m: any) => m && m.role === "system") : []
|
||||
const sysIn = Array.isArray(body.messages)
|
||||
? body.messages.filter((m: any) => m && m.role === "system")
|
||||
: []
|
||||
let ccCount = 0
|
||||
const cc = () => {
|
||||
ccCount++
|
||||
@@ -354,7 +367,9 @@ export function fromAnthropicResponse(resp: any): CommonResponse {
|
||||
|
||||
const idIn = (resp as any).id
|
||||
const id =
|
||||
typeof idIn === "string" ? idIn.replace(/^msg_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}`
|
||||
typeof idIn === "string"
|
||||
? idIn.replace(/^msg_/, "chatcmpl_")
|
||||
: `chatcmpl_${Math.random().toString(36).slice(2)}`
|
||||
const model = (resp as any).model
|
||||
|
||||
const blocks: any[] = Array.isArray((resp as any).content) ? (resp as any).content : []
|
||||
@@ -397,7 +412,9 @@ export function fromAnthropicResponse(resp: any): CommonResponse {
|
||||
const ct = typeof (u as any).output_tokens === "number" ? (u as any).output_tokens : undefined
|
||||
const total = pt != null && ct != null ? pt + ct : undefined
|
||||
const cached =
|
||||
typeof (u as any).cache_read_input_tokens === "number" ? (u as any).cache_read_input_tokens : undefined
|
||||
typeof (u as any).cache_read_input_tokens === "number"
|
||||
? (u as any).cache_read_input_tokens
|
||||
: undefined
|
||||
const details = cached != null ? { cached_tokens: cached } : undefined
|
||||
return {
|
||||
prompt_tokens: pt,
|
||||
@@ -452,7 +469,12 @@ export function toAnthropicResponse(resp: CommonResponse) {
|
||||
} catch {
|
||||
input = (tc as any).function.arguments
|
||||
}
|
||||
content.push({ type: "tool_use", id: (tc as any).id, name: (tc as any).function.name, input })
|
||||
content.push({
|
||||
type: "tool_use",
|
||||
id: (tc as any).id,
|
||||
name: (tc as any).function.name,
|
||||
input,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -511,13 +533,22 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string {
|
||||
if (json.type === "content_block_start") {
|
||||
const cb = json.content_block
|
||||
if (cb?.type === "text") {
|
||||
out.choices.push({ index: json.index ?? 0, delta: { role: "assistant", content: "" }, finish_reason: null })
|
||||
out.choices.push({
|
||||
index: json.index ?? 0,
|
||||
delta: { role: "assistant", content: "" },
|
||||
finish_reason: null,
|
||||
})
|
||||
} else if (cb?.type === "tool_use") {
|
||||
out.choices.push({
|
||||
index: json.index ?? 0,
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{ index: json.index ?? 0, id: cb.id, type: "function", function: { name: cb.name, arguments: "" } },
|
||||
{
|
||||
index: json.index ?? 0,
|
||||
id: cb.id,
|
||||
type: "function",
|
||||
function: { name: cb.name, arguments: "" },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
@@ -532,7 +563,9 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string {
|
||||
} else if (d?.type === "input_json_delta") {
|
||||
out.choices.push({
|
||||
index: json.index ?? 0,
|
||||
delta: { tool_calls: [{ index: json.index ?? 0, function: { arguments: d.partial_json } }] },
|
||||
delta: {
|
||||
tool_calls: [{ index: json.index ?? 0, function: { arguments: d.partial_json } }],
|
||||
},
|
||||
finish_reason: null,
|
||||
})
|
||||
}
|
||||
@@ -558,7 +591,9 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string {
|
||||
prompt_tokens: u.input_tokens,
|
||||
completion_tokens: u.output_tokens,
|
||||
total_tokens: (u.input_tokens || 0) + (u.output_tokens || 0),
|
||||
...(u.cache_read_input_tokens ? { prompt_tokens_details: { cached_tokens: u.cache_read_input_tokens } } : {}),
|
||||
...(u.cache_read_input_tokens
|
||||
? { prompt_tokens_details: { cached_tokens: u.cache_read_input_tokens } }
|
||||
: {}),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,8 @@ export const oaCompatHelper = {
|
||||
const inputTokens = usage.prompt_tokens ?? 0
|
||||
const outputTokens = usage.completion_tokens ?? 0
|
||||
const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? undefined
|
||||
const cacheReadTokens = usage.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? undefined
|
||||
const cacheReadTokens =
|
||||
usage.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? undefined
|
||||
return {
|
||||
inputTokens: inputTokens - (cacheReadTokens ?? 0),
|
||||
outputTokens,
|
||||
@@ -79,7 +80,8 @@ export function fromOaCompatibleRequest(body: any): CommonRequest {
|
||||
if (!m || !m.role) continue
|
||||
|
||||
if (m.role === "system") {
|
||||
if (typeof m.content === "string" && m.content.length > 0) msgsOut.push({ role: "system", content: m.content })
|
||||
if (typeof m.content === "string" && m.content.length > 0)
|
||||
msgsOut.push({ role: "system", content: m.content })
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -90,10 +92,12 @@ export function fromOaCompatibleRequest(body: any): CommonRequest {
|
||||
const parts: any[] = []
|
||||
for (const p of m.content) {
|
||||
if (!p || !p.type) continue
|
||||
if (p.type === "text" && typeof p.text === "string") parts.push({ type: "text", text: p.text })
|
||||
if (p.type === "text" && typeof p.text === "string")
|
||||
parts.push({ type: "text", text: p.text })
|
||||
if (p.type === "image_url") parts.push({ type: "image_url", image_url: p.image_url })
|
||||
}
|
||||
if (parts.length === 1 && parts[0].type === "text") msgsOut.push({ role: "user", content: parts[0].text })
|
||||
if (parts.length === 1 && parts[0].type === "text")
|
||||
msgsOut.push({ role: "user", content: parts[0].text })
|
||||
else if (parts.length > 0) msgsOut.push({ role: "user", content: parts })
|
||||
}
|
||||
continue
|
||||
@@ -137,7 +141,8 @@ export function toOaCompatibleRequest(body: CommonRequest) {
|
||||
if (p.type === "image_url" && p.image_url) return { type: "image_url", image_url: p.image_url }
|
||||
const s = (p as any).source
|
||||
if (!s || typeof s !== "object") return undefined
|
||||
if (s.type === "url" && typeof s.url === "string") return { type: "image_url", image_url: { url: s.url } }
|
||||
if (s.type === "url" && typeof s.url === "string")
|
||||
return { type: "image_url", image_url: { url: s.url } }
|
||||
if (s.type === "base64" && typeof s.media_type === "string" && typeof s.data === "string")
|
||||
return { type: "image_url", image_url: { url: `data:${s.media_type};base64,${s.data}` } }
|
||||
return undefined
|
||||
@@ -147,7 +152,8 @@ export function toOaCompatibleRequest(body: CommonRequest) {
|
||||
if (!m || !m.role) continue
|
||||
|
||||
if (m.role === "system") {
|
||||
if (typeof m.content === "string" && m.content.length > 0) msgsOut.push({ role: "system", content: m.content })
|
||||
if (typeof m.content === "string" && m.content.length > 0)
|
||||
msgsOut.push({ role: "system", content: m.content })
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -160,11 +166,13 @@ export function toOaCompatibleRequest(body: CommonRequest) {
|
||||
const parts: any[] = []
|
||||
for (const p of m.content) {
|
||||
if (!p || !p.type) continue
|
||||
if (p.type === "text" && typeof p.text === "string") parts.push({ type: "text", text: p.text })
|
||||
if (p.type === "text" && typeof p.text === "string")
|
||||
parts.push({ type: "text", text: p.text })
|
||||
const ip = toImg(p)
|
||||
if (ip) parts.push(ip)
|
||||
}
|
||||
if (parts.length === 1 && parts[0].type === "text") msgsOut.push({ role: "user", content: parts[0].text })
|
||||
if (parts.length === 1 && parts[0].type === "text")
|
||||
msgsOut.push({ role: "user", content: parts[0].text })
|
||||
else if (parts.length > 0) msgsOut.push({ role: "user", content: parts })
|
||||
}
|
||||
continue
|
||||
@@ -317,7 +325,9 @@ export function toOaCompatibleResponse(resp: CommonResponse) {
|
||||
|
||||
const idIn = (resp as any).id
|
||||
const id =
|
||||
typeof idIn === "string" ? idIn.replace(/^msg_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}`
|
||||
typeof idIn === "string"
|
||||
? idIn.replace(/^msg_/, "chatcmpl_")
|
||||
: `chatcmpl_${Math.random().toString(36).slice(2)}`
|
||||
const model = (resp as any).model
|
||||
|
||||
const blocks: any[] = Array.isArray((resp as any).content) ? (resp as any).content : []
|
||||
@@ -359,7 +369,8 @@ export function toOaCompatibleResponse(resp: CommonResponse) {
|
||||
const pt = typeof u.input_tokens === "number" ? u.input_tokens : undefined
|
||||
const ct = typeof u.output_tokens === "number" ? u.output_tokens : undefined
|
||||
const total = pt != null && ct != null ? pt + ct : undefined
|
||||
const cached = typeof u.cache_read_input_tokens === "number" ? u.cache_read_input_tokens : undefined
|
||||
const cached =
|
||||
typeof u.cache_read_input_tokens === "number" ? u.cache_read_input_tokens : undefined
|
||||
const details = cached != null ? { cached_tokens: cached } : undefined
|
||||
return {
|
||||
prompt_tokens: pt,
|
||||
@@ -532,7 +543,9 @@ export function toOaCompatibleChunk(chunk: CommonChunk): string {
|
||||
total_tokens: chunk.usage.total_tokens,
|
||||
...(chunk.usage.prompt_tokens_details?.cached_tokens
|
||||
? {
|
||||
prompt_tokens_details: { cached_tokens: chunk.usage.prompt_tokens_details.cached_tokens },
|
||||
prompt_tokens_details: {
|
||||
cached_tokens: chunk.usage.prompt_tokens_details.cached_tokens,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}
|
||||
|
||||
@@ -77,13 +77,20 @@ export function fromOpenaiRequest(body: any): CommonRequest {
|
||||
typeof (s as any).media_type === "string" &&
|
||||
typeof (s as any).data === "string"
|
||||
)
|
||||
return { type: "image_url", image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` } }
|
||||
return {
|
||||
type: "image_url",
|
||||
image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` },
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
const msgs: any[] = []
|
||||
|
||||
const inMsgs = Array.isArray(body.input) ? body.input : Array.isArray(body.messages) ? body.messages : []
|
||||
const inMsgs = Array.isArray(body.input)
|
||||
? body.input
|
||||
: Array.isArray(body.messages)
|
||||
? body.messages
|
||||
: []
|
||||
|
||||
for (const m of inMsgs) {
|
||||
if (!m) continue
|
||||
@@ -96,7 +103,9 @@ export function fromOpenaiRequest(body: any): CommonRequest {
|
||||
const args = typeof a === "string" ? a : JSON.stringify(a ?? {})
|
||||
msgs.push({
|
||||
role: "assistant",
|
||||
tool_calls: [{ id: (m as any).id, type: "function", function: { name, arguments: args } }],
|
||||
tool_calls: [
|
||||
{ id: (m as any).id, type: "function", function: { name, arguments: args } },
|
||||
],
|
||||
})
|
||||
}
|
||||
if ((m as any).type === "function_call_output") {
|
||||
@@ -113,7 +122,8 @@ export function fromOpenaiRequest(body: any): CommonRequest {
|
||||
if (typeof c === "string" && c.length > 0) msgs.push({ role: "system", content: c })
|
||||
if (Array.isArray(c)) {
|
||||
const t = c.find((p: any) => p && typeof p.text === "string")
|
||||
if (t && typeof t.text === "string" && t.text.length > 0) msgs.push({ role: "system", content: t.text })
|
||||
if (t && typeof t.text === "string" && t.text.length > 0)
|
||||
msgs.push({ role: "system", content: t.text })
|
||||
}
|
||||
continue
|
||||
}
|
||||
@@ -126,18 +136,24 @@ export function fromOpenaiRequest(body: any): CommonRequest {
|
||||
const parts: any[] = []
|
||||
for (const p of c) {
|
||||
if (!p || !(p as any).type) continue
|
||||
if (((p as any).type === "text" || (p as any).type === "input_text") && typeof (p as any).text === "string")
|
||||
if (
|
||||
((p as any).type === "text" || (p as any).type === "input_text") &&
|
||||
typeof (p as any).text === "string"
|
||||
)
|
||||
parts.push({ type: "text", text: (p as any).text })
|
||||
const ip = toImg(p)
|
||||
if (ip) parts.push(ip)
|
||||
if ((p as any).type === "tool_result") {
|
||||
const id = (p as any).tool_call_id
|
||||
const content =
|
||||
typeof (p as any).content === "string" ? (p as any).content : JSON.stringify((p as any).content)
|
||||
typeof (p as any).content === "string"
|
||||
? (p as any).content
|
||||
: JSON.stringify((p as any).content)
|
||||
msgs.push({ role: "tool", tool_call_id: id, content })
|
||||
}
|
||||
}
|
||||
if (parts.length === 1 && parts[0].type === "text") msgs.push({ role: "user", content: parts[0].text })
|
||||
if (parts.length === 1 && parts[0].type === "text")
|
||||
msgs.push({ role: "user", content: parts[0].text })
|
||||
else if (parts.length > 0) msgs.push({ role: "user", content: parts })
|
||||
}
|
||||
continue
|
||||
@@ -153,7 +169,11 @@ export function fromOpenaiRequest(body: any): CommonRequest {
|
||||
}
|
||||
|
||||
if ((m as any).role === "tool") {
|
||||
msgs.push({ role: "tool", tool_call_id: (m as any).tool_call_id, content: (m as any).content })
|
||||
msgs.push({
|
||||
role: "tool",
|
||||
tool_call_id: (m as any).tool_call_id,
|
||||
content: (m as any).content,
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -210,7 +230,10 @@ export function toOpenaiRequest(body: CommonRequest) {
|
||||
typeof (s as any).media_type === "string" &&
|
||||
typeof (s as any).data === "string"
|
||||
)
|
||||
return { type: "input_image", image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` } }
|
||||
return {
|
||||
type: "input_image",
|
||||
image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` },
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
@@ -257,7 +280,10 @@ export function toOpenaiRequest(body: CommonRequest) {
|
||||
}
|
||||
|
||||
if ((m as any).role === "tool") {
|
||||
const out = typeof (m as any).content === "string" ? (m as any).content : JSON.stringify((m as any).content)
|
||||
const out =
|
||||
typeof (m as any).content === "string"
|
||||
? (m as any).content
|
||||
: JSON.stringify((m as any).content)
|
||||
input.push({ type: "function_call_output", call_id: (m as any).tool_call_id, output: out })
|
||||
continue
|
||||
}
|
||||
@@ -325,7 +351,9 @@ export function fromOpenaiResponse(resp: any): CommonResponse {
|
||||
|
||||
const idIn = (r as any).id
|
||||
const id =
|
||||
typeof idIn === "string" ? idIn.replace(/^resp_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}`
|
||||
typeof idIn === "string"
|
||||
? idIn.replace(/^resp_/, "chatcmpl_")
|
||||
: `chatcmpl_${Math.random().toString(36).slice(2)}`
|
||||
const model = (r as any).model ?? (resp as any).model
|
||||
|
||||
const out = Array.isArray((r as any).output) ? (r as any).output : []
|
||||
@@ -452,7 +480,9 @@ export function toOpenaiResponse(resp: CommonResponse) {
|
||||
})()
|
||||
|
||||
return {
|
||||
id: (resp as any).id?.replace(/^chatcmpl_/, "resp_") ?? `resp_${Math.random().toString(36).slice(2)}`,
|
||||
id:
|
||||
(resp as any).id?.replace(/^chatcmpl_/, "resp_") ??
|
||||
`resp_${Math.random().toString(36).slice(2)}`,
|
||||
object: "response",
|
||||
model: (resp as any).model,
|
||||
output: outputItems,
|
||||
@@ -498,7 +528,9 @@ export function fromOpenaiChunk(chunk: string): CommonChunk | string {
|
||||
if (typeof name === "string" && name.length > 0) {
|
||||
out.choices.push({
|
||||
index: 0,
|
||||
delta: { tool_calls: [{ index: 0, id, type: "function", function: { name, arguments: "" } }] },
|
||||
delta: {
|
||||
tool_calls: [{ index: 0, id, type: "function", function: { name, arguments: "" } }],
|
||||
},
|
||||
finish_reason: null,
|
||||
})
|
||||
}
|
||||
@@ -555,7 +587,12 @@ export function toOpenaiChunk(chunk: CommonChunk): string {
|
||||
const model = chunk.model
|
||||
|
||||
if (d.content) {
|
||||
const data = { id, type: "response.output_text.delta", delta: d.content, response: { id, model } }
|
||||
const data = {
|
||||
id,
|
||||
type: "response.output_text.delta",
|
||||
delta: d.content,
|
||||
response: { id, model },
|
||||
}
|
||||
return `event: response.output_text.delta\ndata: ${JSON.stringify(data)}`
|
||||
}
|
||||
|
||||
@@ -565,7 +602,13 @@ export function toOpenaiChunk(chunk: CommonChunk): string {
|
||||
const data = {
|
||||
type: "response.output_item.added",
|
||||
output_index: 0,
|
||||
item: { id: tc.id, type: "function_call", name: tc.function.name, call_id: tc.id, arguments: "" },
|
||||
item: {
|
||||
id: tc.id,
|
||||
type: "function_call",
|
||||
name: tc.function.name,
|
||||
call_id: tc.id,
|
||||
arguments: "",
|
||||
},
|
||||
}
|
||||
return `event: response.output_item.added\ndata: ${JSON.stringify(data)}`
|
||||
}
|
||||
@@ -593,7 +636,11 @@ export function toOpenaiChunk(chunk: CommonChunk): string {
|
||||
}
|
||||
: undefined
|
||||
|
||||
const data: any = { id, type: "response.completed", response: { id, model, ...(usage ? { usage } : {}) } }
|
||||
const data: any = {
|
||||
id,
|
||||
type: "response.completed",
|
||||
response: { id, model, ...(usage ? { usage } : {}) },
|
||||
}
|
||||
return `event: response.completed\ndata: ${JSON.stringify(data)}`
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ body {
|
||||
--font-size-9xl: 8rem;
|
||||
|
||||
--font-mono:
|
||||
"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
"IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
|
||||
"Courier New", monospace;
|
||||
--font-sans: var(--font-mono);
|
||||
}
|
||||
|
||||
2
packages/console/app/sst-env.d.ts
vendored
2
packages/console/app/sst-env.d.ts
vendored
@@ -6,4 +6,4 @@
|
||||
/// <reference path="../../../sst-env.d.ts" />
|
||||
|
||||
import "sst"
|
||||
export {}
|
||||
export {}
|
||||
|
||||
Reference in New Issue
Block a user