Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ For Claude, browser cookies (the `sessionKey`) are preferred, because they match
| FLORA | API Key + workspace ID | Workspace available balance, member limit, month-to-date spend |
| Future AGI | API Key + secret key | Billing-period AI credits and gateway requests against free allowances, estimated cost |
| Munito | Admin API Key | Today's and this month's organization spend against spend limits |
| Soniox | Organization API key | Prepaid USD balance, this month's spend against the monthly budget, billing method |
| Claude Platform | Browser cookies / manual Cookie header | Console API credit and purchased credit, other grants, month-to-date spend |

</details>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ describe("provider icon registry", () => {
"manus",
"parallel",
"replicate",
"soniox",
"synthetic",
"typesafe",
"v0",
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop-tauri/src/components/providers/providerIcons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import replicate from "./icons/ProviderIcon-replicate.svg?raw";
import sailresearch from "./icons/ProviderIcon-sailresearch.svg?raw";
import sakana from "./icons/ProviderIcon-sakana.svg?raw";
import sofya from "./icons/ProviderIcon-sofya.svg?raw";
import soniox from "./icons/ProviderIcon-soniox.svg?raw";
import stepfun from "./icons/ProviderIcon-stepfun.svg?raw";
import sub2api from "./icons/ProviderIcon-sub2api.svg?raw";
import synthetic from "./icons/ProviderIcon-synthetic.svg?raw";
Expand Down Expand Up @@ -198,6 +199,7 @@ const RAW: Record<string, string> = {
sailresearch: tint(sailresearch),
sakana: tint(sakana),
sofya: tint(sofya),
soniox: tint(soniox),
stepfun: tint(stepfun),
sub2api: tint(sub2api),
synthetic: tint(synthetic),
Expand Down Expand Up @@ -305,6 +307,7 @@ export const PROVIDER_ICON_REGISTRY: Record<string, ProviderIcon> = {
codebuddy: { id: "codebuddy", brandColor: "#0052d9", fallbackLetter: "C" },
sakana: { id: "sakana", brandColor: "#2975db", fallbackLetter: "S", svgPath: RAW.sakana },
sofya: { id: "sofya", brandColor: "#b0b820", fallbackLetter: "S", svgPath: RAW.sofya },
soniox: { id: "soniox", brandColor: "#253772", fallbackLetter: "S", svgPath: RAW.soniox },
stepfun: { id: "stepfun", brandColor: "#2196f2", fallbackLetter: "S", svgPath: RAW.stepfun },
sub2api: { id: "sub2api", brandColor: "#14b8a6", fallbackLetter: "S", svgPath: RAW.sub2api },
venice: { id: "venice", brandColor: "#3c8fdd", fallbackLetter: "V", svgPath: RAW.venice },
Expand Down
1 change: 1 addition & 0 deletions apps/desktop-tauri/src/test/providerCatalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,6 @@ export const TEST_PROVIDER_CATALOG: Array<[string, string]> = [
["flora", "FLORA"],
["futureagi", "Future AGI"],
["munito", "Munito"],
["soniox", "Soniox"],
["claudeplatform", "Claude Platform"],
];
42 changes: 42 additions & 0 deletions docs/PROVIDERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,48 @@ and name are missing.
an ISO 8601 timestamp; anything else fails parsing. Response bodies are never
echoed in errors.

### Soniox

Soniox is disabled by default. In Settings → Providers → Soniox, enter an
**organization API key** with the "Balance and spend" and "Usage and limits"
permissions, or set `SONIOX_ORG_API_KEY`. Project API keys cannot read
organization billing. Create keys in the
[organization API guide](https://soniox.com/docs/guides/organization-api#organization-api-keys).
The key is stored with the other API keys and is never written to logs.

For proof runs and fake servers, `SONIOX_BASE_URL` replaces the organization API
base (default `https://api.soniox.com/v1/organization`). It accepts `https://`,
or `http://` only for a loopback host (`127.0.0.1`, `localhost`, `[::1]`), with
no credentials, query, or fragment. The path must end in `/v1/organization`.
Any other value, including a blank one, is ignored with a warning.

The provider makes two read-only requests in order. Refresh never submits
transcription or synthesis work:

```text
GET https://api.soniox.com/v1/organization/balance
GET https://api.soniox.com/v1/organization/usage/monthly
Authorization: Bearer <organization API key>
```

- The **Spend** section shows **Monthly budget** as `$used of $budget` with a
progress bar when the budget is positive. The budget meter itself is hidden on
the card and only feeds the tray and float-bar percentages. Upstream hides it
from the card by default; Windows has no default-hidden usage item, so the
card shows the same numbers as this Spend detail row instead.
- The **Billing** section shows **Prepaid** or **Invoice billing** (value `USD`).
With a zero budget it also shows **Monthly budget** as `$0.00`, and no progress
bar or percentage.
- Cost is this month's spend against the budget, reset at the start of the next
UTC month. Prepaid accounts also report the remaining balance. Invoice billing
accounts omit the balance so a zero prepaid amount is not shown as exhausted
credits. A negative prepaid balance is accepted but not shown.
- 401 reports that authentication is required. 403 reports that the key needs
organization permissions. 429 reports rate limiting. Other HTTP errors report
the status. Amounts must be plain decimal strings, `month` must be `YYYY-MM`,
and `invoice_billing_enabled` must be a boolean; anything else fails parsing.
Response bodies are never echoed in errors.

### Claude Platform

Claude Platform is disabled by default and shows the prepaid API credits of a
Expand Down
3 changes: 2 additions & 1 deletion rust/src/core/provider/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ pub enum ProviderId {
FutureAgi,
Munito,
ClaudePlatform,
Soniox,
}

impl ProviderId {
Expand Down Expand Up @@ -304,7 +305,7 @@ mod tests {
#[test]
fn test_provider_id_all() {
let all = ProviderId::all();
assert_eq!(all.len(), 111);
assert_eq!(all.len(), 112);
assert!(all.contains(&ProviderId::Claude));
assert!(all.contains(&ProviderId::Codex));
assert!(all.contains(&ProviderId::Pi));
Expand Down
8 changes: 6 additions & 2 deletions rust/src/core/provider/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,11 @@ const fn spec(
}
}

const COUNT: usize = 111;
const COUNT: usize = 112;

pub(crate) static ALL: [ProviderId; COUNT] = {
// Indexing SPECS by discriminant relies on rows following declaration order.
assert!(P::ClaudePlatform as usize + 1 == COUNT);
assert!(P::Soniox as usize + 1 == COUNT);
let mut ids = [P::Codex; COUNT];
let mut i = 0;
while i < COUNT {
Expand Down Expand Up @@ -778,4 +778,8 @@ pub(crate) static SPECS: [ProviderSpec; COUNT] = [
.status("https://status.claude.com/")
.aliases(&["claude-platform"])
.parse_aliases(&["claude platform"]),
spec(P::Soniox, "soniox", "Soniox", "#253772")
.labels("Monthly budget", "Usage")
.dashboard("https://console.soniox.com")
.status("https://status.soniox.com"),
];
12 changes: 8 additions & 4 deletions rust/src/core/provider_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ use crate::providers::{
NousProvider, OllamaProvider, OpenAIApiProvider, OpenCodeGoProvider, OpenCodeProvider,
OpenRouterProvider, PerplexityProvider, PiProvider, PoeProvider, QoderProvider,
QwenCloudProvider, RaycastProvider, ReplicateProvider, SailResearchProvider, SakanaProvider,
SofyaProvider, StepFunProvider, Sub2ApiProvider, T3ChatProvider, TinyApiProvider,
TypeSafeProvider, V0Provider, VeniceProvider, VercelProvider, VertexAIProvider, WarpProvider,
WayfinderProvider, WindsurfProvider, XKiroProvider, XaiProvider, XapiProvider, ZaiProvider,
ZedProvider, ZenMuxProvider, ZoomMateProvider,
SofyaProvider, SonioxProvider, StepFunProvider, Sub2ApiProvider, T3ChatProvider,
TinyApiProvider, TypeSafeProvider, V0Provider, VeniceProvider, VercelProvider,
VertexAIProvider, WarpProvider, WayfinderProvider, WindsurfProvider, XKiroProvider,
XaiProvider, XapiProvider, ZaiProvider, ZedProvider, ZenMuxProvider, ZoomMateProvider,
};

/// Instantiate the concrete [`Provider`] implementation for a given [`ProviderId`].
Expand Down Expand Up @@ -164,6 +164,7 @@ pub fn instantiate(id: ProviderId) -> Box<dyn Provider> {
ProviderId::Flora => Box::new(FloraProvider::new()),
ProviderId::FutureAgi => Box::new(FutureAgiProvider::new()),
ProviderId::Munito => Box::new(MunitoProvider::new()),
ProviderId::Soniox => Box::new(SonioxProvider::new()),
ProviderId::ClaudePlatform => Box::new(ClaudePlatformProvider::new()),
}
}
Expand Down Expand Up @@ -285,6 +286,7 @@ pub fn pace_capability(id: ProviderId) -> ProviderPaceCapability {
| ProviderId::Flora
| ProviderId::FutureAgi
| ProviderId::Munito
| ProviderId::Soniox
| ProviderId::ClaudePlatform => ProviderPaceCapability::UNSUPPORTED,
}
}
Expand Down Expand Up @@ -406,6 +408,7 @@ pub fn limit_reset_policy(id: ProviderId) -> ProviderLimitResetPolicy {
| ProviderId::Flora
| ProviderId::FutureAgi
| ProviderId::Munito
| ProviderId::Soniox
| ProviderId::ClaudePlatform => ProviderLimitResetPolicy::DEFAULT,
}
}
Expand Down Expand Up @@ -527,6 +530,7 @@ pub(crate) fn cost_cli_extension_for(id: ProviderId) -> Option<&'static dyn Cost
| ProviderId::Flora
| ProviderId::FutureAgi
| ProviderId::Munito
| ProviderId::Soniox
| ProviderId::ClaudePlatform => None,
}
}
Expand Down
4 changes: 4 additions & 0 deletions rust/src/core/provider_registry_snapshot.txt
Original file line number Diff line number Diff line change
Expand Up @@ -442,3 +442,7 @@ ClaudePlatform cli=claudeplatform display="Claude Platform" cookie=Some("platfor
map=["claude-platform", "claudeplatform"]
metadata=ProviderMetadata { id: ClaudePlatform, display_name: "Claude Platform", session_label: "API credit", weekly_label: "Purchased credit", supports_opus: false, supports_credits: false, default_enabled: false, is_primary: false, dashboard_url: Some("https://platform.claude.com/settings/billing"), status_page_url: Some("https://status.claude.com/"), tertiary_label_key: None }
sources=[Auto, Web] oauth=false web=true cli=false
Soniox cli=soniox display="Soniox" cookie=None color=#253772 deprecated=false blocking=None
map=["soniox"]
metadata=ProviderMetadata { id: Soniox, display_name: "Soniox", session_label: "Monthly budget", weekly_label: "Usage", supports_opus: false, supports_credits: false, default_enabled: false, is_primary: false, dashboard_url: Some("https://console.soniox.com"), status_page_url: Some("https://status.soniox.com"), tertiary_label_key: None }
sources=[Auto, OAuth] oauth=false web=false cli=false
1 change: 1 addition & 0 deletions rust/src/core/token_accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ impl TokenAccountSupport {
| ProviderId::Flora
| ProviderId::FutureAgi
| ProviderId::Munito
| ProviderId::Soniox
| ProviderId::ClaudePlatform => None,
}
}
Expand Down
2 changes: 2 additions & 0 deletions rust/src/providers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ pub mod replicate;
pub mod sailresearch;
pub mod sakana;
pub mod sofya;
pub mod soniox;
pub mod stepfun;
pub mod sub2api;
pub mod synthetic;
Expand Down Expand Up @@ -216,6 +217,7 @@ pub use replicate::ReplicateProvider;
pub use sailresearch::SailResearchProvider;
pub use sakana::SakanaProvider;
pub use sofya::SofyaProvider;
pub use soniox::SonioxProvider;
pub use stepfun::StepFunProvider;
pub use sub2api::Sub2ApiProvider;
pub use synthetic::SyntheticProvider;
Expand Down
Loading