Skip to content

Commit 7af15d8

Browse files
committed
Merge remote-tracking branch 'origin/staging' into wip/merge-staging-9740
# Conflicts: # apps/sim/ee/organization-usage/components/usage-consumers.tsx
2 parents 34f9084 + d27970a commit 7af15d8

26 files changed

Lines changed: 632 additions & 21 deletions

File tree

‎apps/docs/content/docs/platform/costs.mdx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ The settings page groups keys into **Models**, **Search & web**, and **Enrichmen
129129
| Nebius | Model calls |
130130
| Ollama Cloud | Model calls |
131131
| Kie | Model calls |
132+
| Thinking Machines | Model calls |
132133
| Fal.ai | Image and video generation |
133134
</Tab>
134135
<Tab>

‎apps/docs/content/docs/workflows/blocks/agent.mdx‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ Select a model beginning with `nebius/` to run an open model through Nebius Toke
4444

4545
Bring your own Nebius Token Factory API key. On Sim Cloud, a workspace admin can add it under **Workspace settings → BYOK → Nebius**. On self-hosted Sim, provide the key in the Agent block's **API Key** field, using a [secret reference](/platform/credentials). Model features such as image input and structured outputs depend on the selected model.
4646

47+
#### Thinking Machines
48+
49+
Select a model beginning with `thinkingmachines/` to run Inkling, Inkling-Small, or Inkling 2 Small through Tinker serverless inference. To sample a checkpoint you trained on Tinker, enter `thinkingmachines/` followed by its `tinker://` path. Custom checkpoints have no catalog pricing.
50+
51+
Bring your own Thinking Machines Tinker API key. On Sim Cloud, a workspace admin can add it under **Workspace settings → BYOK → Thinking Machines**. On self-hosted Sim, provide the key in the Agent block's **API Key** field, using a [secret reference](/platform/credentials). Tinker's serverless inference is in beta: file attachments are not sent, tools run with automatic tool choice only, and structured outputs are requested through the prompt.
52+
4753
#### Jev evaluation models
4854

4955
Select `jev-latest` from TypeSafe in the Agent model selector. Hosted Sim supplies a key and bills model usage through the normal credit system; workspace or organization BYOK keys override the hosted key without model charges. Self-hosted users enter their TypeSafe key in the block. Use `jev-1.13.0` to pin a version or `jev-preview` to follow preview releases. These models use **State** and **Questions** in place of conversational messages. State accepts text or a reference to a JSON object or array. Questions is a JSON object keyed by the answer names you want:

‎apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
ZeroBounceIcon,
4848
} from '@/components/icons'
4949
import { NebiusIcon } from '@/components/icons/nebius'
50+
import { ThinkingMachinesIcon } from '@/components/icons/thinking-machines'
5051
import { canMutateWorkspaceSettingsSection } from '@/components/settings/navigation'
5152
import { type BYOKProviderId, MAX_BYOK_KEYS_PER_PROVIDER } from '@/lib/api/contracts/byok-keys'
5253
import { useDeploymentShape } from '@/lib/core/config/deployment-shape'
@@ -182,6 +183,13 @@ const PROVIDERS: (BYOKManagerProvider & { id: BYOKProviderId })[] = [
182183
description: 'LLM calls',
183184
placeholder: 'Enter your Kie API key',
184185
},
186+
{
187+
id: 'thinkingmachines',
188+
name: 'Thinking Machines',
189+
icon: ThinkingMachinesIcon,
190+
description: 'LLM calls',
191+
placeholder: 'Enter your Thinking Machines Tinker API key',
192+
},
185193
{
186194
id: 'falai',
187195
name: 'Fal.ai',
@@ -383,6 +391,7 @@ const PROVIDER_SECTIONS: BYOKProviderSection[] = [
383391
'nebius',
384392
'ollama-cloud',
385393
'kie',
394+
'thinkingmachines',
386395
'falai',
387396
],
388397
},

‎apps/sim/components/icons/providers.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
ZaiIcon,
2828
} from '@/components/icons'
2929
import { NebiusIcon } from '@/components/icons/nebius'
30+
import { ThinkingMachinesIcon } from '@/components/icons/thinking-machines'
3031

3132
/**
3233
* Provider brand marks, keyed by the `providerId` the server resolves.
@@ -60,6 +61,7 @@ export const PROVIDER_ICONS: Readonly<Record<string, ComponentType<{ className?:
6061
openai: OpenAIIcon,
6162
openrouter: OpenRouterIcon,
6263
sakana: SakanaIcon,
64+
thinkingmachines: ThinkingMachinesIcon,
6365
together: TogetherIcon,
6466
typesafe: TypeSafeIcon,
6567
vertex: VertexIcon,
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { SVGProps } from 'react'
2+
3+
interface ThinkingMachinesIconProps extends SVGProps<SVGSVGElement> {}
4+
5+
export function ThinkingMachinesIcon(props: ThinkingMachinesIconProps) {
6+
return (
7+
<svg {...props} viewBox='-4 -4 99.7998 42' fill='none' xmlns='http://www.w3.org/2000/svg'>
8+
<rect x='-4' y='-4' width='99.7998' height='42' rx='4' fill='#FFFFFF' />
9+
<path
10+
d='M5.77637 30.1113H5.8125L9.61621 21.5127H11.6064V34H9.63379V25.6514H9.59863L6.46973 32.4121H5.11914L2.00879 25.6514H1.97266V34H0V21.5127H1.97266L5.77637 30.1113ZM24.8838 34H22.7861L21.7197 31.0391H17.0811L16.0146 34H13.917L18.4316 21.5127H20.3867L24.8838 34ZM36.5635 23.4922V25.1338H34.501V24.2236L33.5596 23.2959H29.8262L28.8135 24.2949V31.2168L29.8262 32.2158H33.5596L34.501 31.2881V30.3789H36.5635V32.0195L34.5898 34H28.7598L26.752 31.9844V23.5283L28.7598 21.5127H34.5898L36.5635 23.4922ZM41.3877 26.8818H47.04V21.5127H49.1025V34H47.04V28.666H41.3877V34H39.3262V21.5127H41.3877V26.8818ZM54.6377 34H52.5762V21.5127H54.6377V34ZM65.8467 30.5928H65.8828V21.5127H67.8555V34H65.9717L60.1055 24.9375H60.0703V34H58.0977V21.5127H59.9814L65.8467 30.5928ZM79.915 23.2783H73.3027V26.8467H79.3994V28.5947H73.3027V32.2334H79.915V34H71.2412V21.5127H79.915V23.2783ZM91.6582 23.3496V24.9375H89.6318V23.9746L88.9385 23.2959H85.2412L84.5479 23.9746V26.0439L85.2412 26.7217H89.9688L91.7998 28.5586V32.127L89.9336 34H84.3164L82.4863 32.1621V30.5928H84.5127V31.5381L85.1875 32.2158H89.0625L89.7734 31.5029V29.2012L89.0801 28.5059H84.3525L82.5215 26.6855V23.3496L84.3525 21.5127H89.8271L91.6582 23.3496ZM17.5781 29.3086H21.2402L19.4092 24.0273H19.374L17.5781 29.3086ZM12.7705 1.76562H9.03711V12.4873H6.97559V1.76562H3.24316V0H12.7705V1.76562ZM17.3232 5.36914H22.9756V0H25.0381V12.4873H22.9756V7.15332H17.3232V12.4873H15.2617V0H17.3232V5.36914ZM30.5732 12.4873H28.5117V0H30.5732V12.4873ZM41.7822 9.08008H41.8184V0H43.791V12.4873H41.9072L36.041 3.4248H36.0059V12.4873H34.0322V0H35.917L41.7822 9.08008ZM49.2383 5.36914H51.46L54.5352 0H56.8281L53.2197 6.22559L57.1299 12.4873H54.7832L51.46 7.15332H49.2383V12.4873H47.1758V0H49.2383V5.36914ZM61.585 12.4873H59.5234V0H61.585V12.4873ZM72.7939 9.08008H72.8301V0H74.8027V12.4873H72.9189L67.0527 3.4248H67.0176V12.4873H65.0449V0H66.9287L72.7939 9.08008ZM87.9287 1.98047V3.62109H85.8662V2.71191L84.9248 1.78418H81.085L80.0723 2.7832V9.70508L81.085 10.7041H84.9248L85.9199 9.72266V7.45703H83.2002V5.67285H87.9287V10.4717L85.9199 12.4873H80.0186L78.0107 10.4717V2.01562L80.0186 0H85.9551L87.9287 1.98047Z'
11+
fill='#000000'
12+
/>
13+
</svg>
14+
)
15+
}

‎apps/sim/lib/api-key/byok.ts‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,20 @@ export async function getApiKeyWithBYOK(
345345
throw new Error(`API key is required for Nebius ${model}`)
346346
}
347347

348+
if (provider === 'thinkingmachines') {
349+
const byokResult = await getBYOKKey(workspaceId, 'thinkingmachines', { signal })
350+
if (byokResult) {
351+
logger.info('Using BYOK key for Thinking Machines', {
352+
model,
353+
workspaceId,
354+
scope: byokResult.scope,
355+
})
356+
return byokResult
357+
}
358+
if (userProvidedKey) return { apiKey: userProvidedKey, isBYOK: false }
359+
throw new Error(`API key is required for Thinking Machines ${model}`)
360+
}
361+
348362
if (provider === 'ollama-cloud') {
349363
if (workspaceId) {
350364
const byokResult = await getBYOKKey(workspaceId, 'ollama-cloud', { signal })

‎apps/sim/lib/api/contracts/byok-keys.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const byokProviderIdSchema = z.enum([
1717
'nebius',
1818
'ollama-cloud',
1919
'kie',
20+
'thinkingmachines',
2021
'falai',
2122
'firecrawl',
2223
'exa',

‎apps/sim/lib/billing/core/chat-access.ts‎

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,30 @@ import { isFree } from '@/lib/billing/plan-helpers'
55
import type { UsageUpgradePayer } from '@/lib/billing/usage-upgrade'
66
import { isBillingEnabled, isHosted } from '@/lib/core/config/env-flags'
77

8-
const logger = createLogger('ChatAccess')
8+
const logger = createLogger('PaidAccess')
9+
10+
interface PaidAccessOptions {
11+
/**
12+
* The verdict for a free payer whose credit balance cannot be read. Chat admits, because a
13+
* database blip must not lock a paying customer out and every turn is bounded by the usage
14+
* check that follows. A hosted key denies, because nothing after acquisition bounds it.
15+
*/
16+
onBalanceUnavailable: 'admit' | 'deny'
17+
}
918

1019
/**
11-
* Whether a payer may use Chat (talking to Sim). Every turn spends hosted model credits, so a
12-
* hosted deployment that bills withholds Chat from a payer on the free plan — unless that payer
13-
* holds prepaid credits, which an admin grants to customers billed outside a subscription.
20+
* Whether a payer may use a feature a hosted deployment reserves for paying payers: Chat, and
21+
* hosted keys a tool marks `requiresPaidPlan`. A hosted deployment that bills withholds these from
22+
* a payer on the free plan, unless that payer holds prepaid credits, which an admin grants to
23+
* customers billed outside a subscription.
1424
*
1525
* The plan is the payer's entitled subscription (active or past due), so a canceled
1626
* plan reads as free; a blocked payer is refused by the usage check every caller runs next.
17-
*
18-
* This is the single verdict behind every Chat plan gate: run admission, chat titles, scheduled
19-
* job dispatch, Copilot key admission, and the workspace host context the composer reads, so the
20-
* composer never offers a send that admission will refuse. A balance that cannot be read admits
21-
* the payer: a database blip must not lock a paying customer out, and a free payer stays bounded
22-
* by its usage limit.
2327
*/
24-
export async function payerHasChatAccess(payer: UsageUpgradePayer): Promise<boolean> {
28+
export async function payerHasPaidAccess(
29+
payer: UsageUpgradePayer,
30+
options: PaidAccessOptions
31+
): Promise<boolean> {
2532
if (!isHosted || !isBillingEnabled) return true
2633
if (!isFree(payer.payerSubscription?.plan)) return true
2734
try {
@@ -31,10 +38,23 @@ export async function payerHasChatAccess(payer: UsageUpgradePayer): Promise<bool
3138
)
3239
return balance > 0
3340
} catch (error) {
34-
logger.warn('Credit balance unavailable; admitting the payer to Chat', {
41+
logger.warn('Credit balance unavailable for a free payer', {
3542
billingEntity: payer.billingEntity,
43+
verdict: options.onBalanceUnavailable,
3644
error: getErrorMessage(error),
3745
})
38-
return true
46+
return options.onBalanceUnavailable === 'admit'
3947
}
4048
}
49+
50+
/**
51+
* Whether a payer may use Chat (talking to Sim): {@link payerHasPaidAccess}, since every turn
52+
* spends hosted model credits, admitting a payer whose balance cannot be read.
53+
*
54+
* This is the single verdict behind every Chat plan gate: run admission, chat titles, scheduled
55+
* job dispatch, Copilot key admission, and the workspace host context the composer reads, so the
56+
* composer never offers a send that admission will refuse.
57+
*/
58+
export function payerHasChatAccess(payer: UsageUpgradePayer): Promise<boolean> {
59+
return payerHasPaidAccess(payer, { onBalanceUnavailable: 'admit' })
60+
}

‎apps/sim/lib/mothership/generated/protocol.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,27 @@ export const SteerV2Response = z.discriminatedUnion("status", [
569569
]);
570570
export type SteerV2Response = z.infer<typeof SteerV2Response>;
571571

572+
/**
573+
* POST /api/streams/steer/v2/disposition: what became of the input `steeringId`, read without
574+
* sending it. It never queues, renders or claims anything. An input the chat holds answers as
575+
* a retry of it on /api/streams/steer/v2 would (the chat-wide identity included). One it does
576+
* not hold answers `not_applied` once the run `messageId` has ended or stopped, since no
577+
* steer could reach it any more, and `unknown` while the run is live: nothing was delivered,
578+
* and sending it is still the caller's choice. A run that does not exist (or another
579+
* tenant's) is a 404 `steering_target_missing`, as for a steer.
580+
*/
581+
export const SteerDispositionRequest = z.strictObject({
582+
messageId: z.uuid(),
583+
steeringId: z.uuid(),
584+
});
585+
export type SteerDispositionRequest = z.infer<typeof SteerDispositionRequest>;
586+
587+
export const SteerDispositionResponse = z.discriminatedUnion("status", [
588+
...SteerV2Response.options,
589+
z.strictObject({ steeringId: z.string(), status: z.literal("unknown") }),
590+
]);
591+
export type SteerDispositionResponse = z.infer<typeof SteerDispositionResponse>;
592+
572593
/** The `run`/`steering_applied` frame: a steer reached the run's model input. */
573594
export interface StreamSteeringApplied {
574595
kind: "steering_applied";

‎apps/sim/lib/sim-search/live/providers.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,12 @@ export const LIVE_SEARCH_PROVIDERS = {
185185
confluence: {
186186
guide: {
187187
syntax:
188-
'CQL: text ~ "term", title ~ "term" (title ~ "win*" for a prefix), type IN (page, blogpost) and space = KEY (quote keys starting with a digit), joined with AND/OR/NOT and parentheses; add ORDER BY lastmodified DESC only when recency matters more than relevance.',
188+
'CQL: siteSearch ~ "a few words" is Confluence relevance search and ranks pages about the topic first, so use it for topic, acronym and how-to lookups. text ~ "term" and title ~ "term" (title ~ "win*" for a prefix) match pages containing those words or their variants without that ranking, so keep them for a specific identifier or term; text ~ "\\"exact phrase\\"" matches a phrase. Combine with type IN (page, blogpost) and space = KEY (quote keys starting with a digit) using AND/OR/NOT and parentheses; add ORDER BY lastmodified DESC only when recency matters more than relevance.',
189189
scope:
190190
'creator = currentUser(), contributor = currentUser(), mention = currentUser() and space = KEY. CQL has no project field; the separate native project field takes an Atlassian site cloud ID.',
191-
example: 'type IN (page, blogpost) AND space = ENG AND text ~ "roadmap"',
191+
example: 'type IN (page, blogpost) AND siteSearch ~ "deployment freeze process"',
192192
avoid:
193-
'starting the query with a negative clause (NOT, !=, !~ or NOT IN), which CQL rejects; lead with a positive clause such as type IN (page, blogpost).',
193+
'ORing several text ~ clauses for a topic, which ranks pages sharing one common word first (put the words in one siteSearch instead), and starting the query with a negative clause (NOT, !=, !~ or NOT IN), which CQL rejects; lead with a positive clause such as type IN (page, blogpost).',
194194
},
195195
requiresEveryTerm: true,
196196
previewText: previewConfluence,

0 commit comments

Comments
 (0)