Compare commits

..
Author SHA1 Message Date
grenadeandClaude Opus 5.5 e568cf9d37 feat(web): rename the Playground to Decisions, at /decisions
CI / Config consistency (push) Successful in 10s
CI / Classify changes (push) Successful in 19s
CI / Format (push) Skipped
CI / Clippy (push) Skipped
CI / Test (push) Skipped
CI / CUDA type-check (push) Skipped
CI / Build cortex SRPM (push) Skipped
CI / Build neuron SRPM (push) Skipped
CI / Publish cortex to COPR (push) Skipped
CI / Publish neuron to COPR (push) Skipped
CI / Bump version in source (push) Skipped
CI / Web (lint + typecheck + i18n + build) (push) Successful in 2m49s
"Playground" said nothing about what the page does. It is now
"Decisions" in the nav and as the page title, at /decisions, matching
Chat, Images and Music. The lead says what you do there in plain words
(yes/no, multiple-choice or scoring questions, instant answers with
calibrated confidence) and mentions once that such models are sometimes
called System One models, a term from Sept 2026 that is not yet widely
known. All 42 locales translated.

/playground is dropped without a redirect (operator decision: no
bookmarks to keep). The API is unchanged: /v1/systemone stays the Jev
wire path. Share links take the page's own path, so they now point at
/decisions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ohK2pULMVJctL5B1Gixdp
2026-10-19 14:55:14 +03:00
grenade a25c20e8c8 Merge branch 'fix/cold-load-serialise': no concurrent cold-loads into an out-of-memory
build-prerelease / Resolve version stamps + change detection (push) Successful in 18s
build-prerelease / Build helexa-bench binary (push) Skipped
build-prerelease / Build helexa-upstream binary (push) Skipped
build-prerelease / Build helexa-angels binary (push) Skipped
build-prerelease / Build helexa-router binary (push) Skipped
build-prerelease / Build helexa-tools binary (push) Skipped
build-prerelease / Build neuron-blackwell (push) Skipped
build-prerelease / Build neuron-ampere (push) Skipped
build-prerelease / Build neuron-ada (push) Skipped
build-prerelease / Package helexa-bench RPM (push) Skipped
build-prerelease / Package helexa-upstream RPM (push) Skipped
build-prerelease / Package helexa-angels RPM (push) Skipped
build-prerelease / Package helexa-router RPM (push) Skipped
build-prerelease / Package helexa-tools RPM (push) Skipped
build-prerelease / Package helexa-neuron-ada RPM (push) Skipped
build-prerelease / Package helexa-neuron-ampere RPM (push) Skipped
build-prerelease / Package helexa-neuron-blackwell RPM (push) Skipped
build-prerelease / Build cortex binary (push) Successful in 2m15s
build-prerelease / Package cortex RPM (push) Successful in 27s
build-prerelease / Lint (fmt + clippy) (push) Successful in 3m12s
build-prerelease / Test (push) Successful in 12m44s
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Successful in 17s
2026-10-19 14:29:49 +03:00
grenadeandClaude Opus 5.5 8bd371824d fix(cortex): placement waits for a neuron's own startup loads
CI / Config consistency (push) Successful in 14s
CI / Classify changes (push) Successful in 23s
CI / Web (lint + typecheck + i18n + build) (push) Skipped
CI / Format (push) Successful in 16s
CI / Clippy (push) Successful in 2m57s
CI / Test (push) Successful in 6m28s
CI / CUDA type-check (push) Successful in 19m25s
CI / Build cortex SRPM (push) Skipped
CI / Build neuron SRPM (push) Skipped
CI / Publish cortex to COPR (push) Skipped
CI / Publish neuron to COPR (push) Skipped
CI / Bump version in source (push) Skipped
Right after a deploy restarted benjy's neuron, a HeartMuLa job
cold-loaded while the neuron was still loading its default Qwen3-8B
(36 s). That load was in no model list and its memory had barely
started to fill, so the card looked empty; both landed and the HeartMuLa
load ran out of memory.

neuron's /health already reports its activation (pending, in_progress).
Placement now counts those models' catalogue footprint as taken, and a
cold-load that does not fit while the node is still activating answers
503 + Retry-After instead of loading over it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ohK2pULMVJctL5B1Gixdp
2026-10-19 14:09:28 +03:00
grenadeandClaude Opus 5.5 f40406e49b fix(cortex): serialise cold-loads per node; never evict a busy model
CI / Config consistency (push) Successful in 12s
CI / Classify changes (push) Successful in 18s
CI / Web (lint + typecheck + i18n + build) (push) Skipped
CI / Format (push) Successful in 14s
CI / Clippy (push) Successful in 3m24s
CI / Test (push) Successful in 7m13s
CI / Build cortex SRPM (push) Canceled after 0s
CI / Build neuron SRPM (push) Canceled after 0s
CI / Publish cortex to COPR (push) Canceled after 0s
CI / Publish neuron to COPR (push) Canceled after 0s
CI / Bump version in source (push) Canceled after 0s
CI / CUDA type-check (push) Canceled after 19m15s
Two music jobs (HeartMuLa, then ACE-Step 90 s later) both cold-loaded
onto benjy's 24 GB card. Placement reads VRAM from the last /health
poll, which could not see HeartMuLa's three-minute load still in
progress, so ACE-Step "fit" without evicting anything. Both landed
(23.8 GB used, 289 MB free) and every render on the card then failed
with CUDA_ERROR_OUT_OF_MEMORY.

- A per-node lock now covers each cold-load's evict -> load -> fresh
  /health. A request that waited re-checks inside the lock: if its model
  is now loaded it routes there; otherwise fit is decided from a fresh
  /health, refreshed again after each eviction.
- The evictor (and placement's evictable estimate) skip models with
  requests running or queued, and models cortex cold-loaded in the last
  30 s for a request that has not reached them yet. Evicting either
  kills work in progress.
- When a model still does not fit only because a model it would
  displace is busy, routing answers 503 + Retry-After (10 s) instead of
  loading into an out-of-memory. Audio jobs treat that as busy and wait
  without spending an attempt.

tests/cold_load.rs drives a mock neuron that keeps VRAM books for one
card and flags over-commit; removing the lock, the eviction guard or the
deferral each fails it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ohK2pULMVJctL5B1Gixdp
2026-10-19 13:49:51 +03:00
grenade 6535efc51f Merge branch 'feat/audio-disk-guard': refuse audio jobs when the store is low on disk
build-prerelease / Resolve version stamps + change detection (push) Successful in 20s
build-prerelease / Build helexa-bench binary (push) Successful in 2m31s
build-prerelease / Build helexa-upstream binary (push) Successful in 2m49s
build-prerelease / Build neuron-ada (push) Successful in 3m0s
build-prerelease / Build cortex binary (push) Successful in 3m5s
build-prerelease / Package helexa-bench RPM (push) Successful in 31s
build-prerelease / Package helexa-upstream RPM (push) Successful in 29s
build-prerelease / Build helexa-angels binary (push) Successful in 3m36s
build-prerelease / Lint (fmt + clippy) (push) Successful in 3m50s
build-prerelease / Package cortex RPM (push) Successful in 35s
build-prerelease / Package helexa-angels RPM (push) Successful in 27s
build-prerelease / Build helexa-tools binary (push) Successful in 1m47s
build-prerelease / Build helexa-router binary (push) Successful in 1m56s
build-prerelease / Package helexa-tools RPM (push) Successful in 21s
build-prerelease / Package helexa-router RPM (push) Successful in 20s
build-prerelease / Build neuron-blackwell (push) Successful in 16m33s
build-prerelease / Test (push) Successful in 18m17s
build-prerelease / Build neuron-ampere (push) Successful in 21m31s
build-prerelease / Package helexa-neuron-ada RPM (push) Successful in 1m17s
build-prerelease / Package helexa-neuron-ampere RPM (push) Successful in 3m59s
build-prerelease / Package helexa-neuron-blackwell RPM (push) Successful in 4m9s
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Successful in 35s
2026-10-19 13:37:02 +03:00
grenade 231c4b3991 Merge branch 'fix/music-hide-stale-on-compose': clear earlier songs when composing
build-prerelease / Resolve version stamps + change detection (push) Successful in 19s
build-prerelease / Lint (fmt + clippy) (push) Skipped
build-prerelease / Test (push) Skipped
build-prerelease / Build cortex binary (push) Skipped
build-prerelease / Build helexa-bench binary (push) Skipped
build-prerelease / Build helexa-upstream binary (push) Skipped
build-prerelease / Build helexa-angels binary (push) Skipped
build-prerelease / Build helexa-router binary (push) Skipped
build-prerelease / Build helexa-tools binary (push) Skipped
build-prerelease / Build neuron-blackwell (push) Skipped
build-prerelease / Build neuron-ampere (push) Skipped
build-prerelease / Build neuron-ada (push) Skipped
build-prerelease / Package cortex RPM (push) Skipped
build-prerelease / Package helexa-bench RPM (push) Skipped
build-prerelease / Package helexa-upstream RPM (push) Skipped
build-prerelease / Package helexa-angels RPM (push) Skipped
build-prerelease / Package helexa-router RPM (push) Skipped
build-prerelease / Package helexa-tools RPM (push) Skipped
build-prerelease / Package helexa-neuron-ada RPM (push) Skipped
build-prerelease / Package helexa-neuron-ampere RPM (push) Skipped
build-prerelease / Package helexa-neuron-blackwell RPM (push) Skipped
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Skipped
2026-10-19 13:23:35 +03:00
grenadeandClaude Opus 5.5 03b3e3e701 fix(web): starting a new song clears earlier ones from the music studio
CI / Classify changes (push) Successful in 18s
CI / Config consistency (push) Successful in 10s
CI / Format (push) Skipped
CI / Clippy (push) Skipped
CI / Test (push) Skipped
CI / CUDA type-check (push) Skipped
CI / Build cortex SRPM (push) Skipped
CI / Build neuron SRPM (push) Skipped
CI / Publish cortex to COPR (push) Skipped
CI / Publish neuron to COPR (push) Skipped
CI / Bump version in source (push) Skipped
CI / Web (lint + typecheck + i18n + build) (push) Successful in 2m57s
After a song finished, its player stayed under the form. Clicking
Surprise me then filled the form with a new idea beside the old song's
player and downloads, which read as the new song already made.

Now a surprise, or any edit to the form (style, chips, lyrics, length,
model, seed, steps), clears earlier songs out of the studio and closes a
song opened from the history. A song still rendering stays in view while
it renders; once finished it goes to the history instead of back into
the studio, so a player never sits beside a form that does not describe
it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ohK2pULMVJctL5B1Gixdp
2026-10-19 13:19:44 +03:00
grenadeandClaude Opus 5.5 1f96bf1d16 feat(cortex): refuse audio jobs when the job store is low on disk
CI / Config consistency (push) Successful in 10s
CI / Classify changes (push) Successful in 17s
CI / Web (lint + typecheck + i18n + build) (push) Skipped
CI / Format (push) Successful in 13s
CI / Clippy (push) Successful in 2m56s
CI / Test (push) Successful in 6m18s
CI / CUDA type-check (push) Successful in 19m39s
CI / Build cortex SRPM (push) Skipped
CI / Build neuron SRPM (push) Skipped
CI / Publish cortex to COPR (push) Skipped
CI / Publish neuron to COPR (push) Skipped
CI / Bump version in source (push) Skipped
Rendered audio is written to [audio_jobs] dir, which on the gateway host
is the root filesystem. A store that filled it would take cortex and the
OS down with it. Submissions are now refused with 503
audio_storage_full and Retry-After: 600 (about one sweep interval) while
that filesystem has less than min_free_mb free (default 10 GiB, 0
disables). The check runs before any budget is reserved, so a refusal
costs the caller nothing; jobs already accepted still finish.

An interim guard: shared object storage for a large fleet is #411.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ohK2pULMVJctL5B1Gixdp
2026-10-19 13:16:13 +03:00
grenade 4431b19461 Merge branch 'feat/music-studio-ux': music studio UX
build-prerelease / Resolve version stamps + change detection (push) Successful in 20s
build-prerelease / Lint (fmt + clippy) (push) Skipped
build-prerelease / Test (push) Skipped
build-prerelease / Build cortex binary (push) Skipped
build-prerelease / Build helexa-bench binary (push) Skipped
build-prerelease / Build helexa-upstream binary (push) Skipped
build-prerelease / Build helexa-angels binary (push) Skipped
build-prerelease / Build helexa-router binary (push) Skipped
build-prerelease / Build helexa-tools binary (push) Skipped
build-prerelease / Build neuron-blackwell (push) Skipped
build-prerelease / Build neuron-ampere (push) Skipped
build-prerelease / Build neuron-ada (push) Skipped
build-prerelease / Package cortex RPM (push) Skipped
build-prerelease / Package helexa-bench RPM (push) Skipped
build-prerelease / Package helexa-upstream RPM (push) Skipped
build-prerelease / Package helexa-angels RPM (push) Skipped
build-prerelease / Package helexa-router RPM (push) Skipped
build-prerelease / Package helexa-tools RPM (push) Skipped
build-prerelease / Package helexa-neuron-ada RPM (push) Skipped
build-prerelease / Package helexa-neuron-ampere RPM (push) Skipped
build-prerelease / Package helexa-neuron-blackwell RPM (push) Skipped
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Skipped
2026-10-19 12:57:49 +03:00
grenadeandClaude Opus 5.5 01a3ddaefa feat(web): music studio — no stale song on load, live activity, waveform player
CI / Config consistency (push) Successful in 10s
CI / Classify changes (push) Successful in 18s
CI / Format (push) Skipped
CI / Clippy (push) Skipped
CI / Test (push) Skipped
CI / CUDA type-check (push) Skipped
CI / Build cortex SRPM (push) Skipped
CI / Build neuron SRPM (push) Skipped
CI / Publish cortex to COPR (push) Skipped
CI / Publish neuron to COPR (push) Skipped
CI / Bump version in source (push) Skipped
CI / Web (lint + typecheck + i18n + build) (push) Successful in 2m54s
The /music page is the place to make songs, so a fresh load no longer
opens the newest old song. The space under the form is now the studio:
an idea being written, a submission in flight, songs rendering (with an
equaliser on their cover), and the songs made this visit. A song is
opened from the history only on request, and can be closed again.

- Generate is visibly disabled (flat, not a faded gradient) while an
  idea is written or a submission is in flight, and when the style is
  empty or the length is out of range; a line under it says which.
- Style and lyrics sit side by side on wide screens.
- Style chips toggle common genre and mood words into the prompt.
- Length is a slider showing m:ss.
- Each song gets a generated cover (stable per id), shown on its card
  and as a swatch in the history.
- A waveform player replaces the native audio control: the waveform is
  decoded from the song and doubles as the seek bar (click or arrow
  keys); before the song is fetched it shows a stand-in shape.
- FLAC and MP3 download without first pressing play.
- The history button no longer covers the title at phone widths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ohK2pULMVJctL5B1Gixdp
2026-10-19 12:54:03 +03:00
grenade 1a1129f3d1 Merge branch 'fix/validate-wait-idle': validate scripts wait for idle before evicting
build-prerelease / Resolve version stamps + change detection (push) Successful in 19s
build-prerelease / Lint (fmt + clippy) (push) Skipped
build-prerelease / Test (push) Skipped
build-prerelease / Build cortex binary (push) Skipped
build-prerelease / Build helexa-bench binary (push) Skipped
build-prerelease / Build helexa-upstream binary (push) Skipped
build-prerelease / Build helexa-angels binary (push) Skipped
build-prerelease / Build helexa-router binary (push) Skipped
build-prerelease / Build helexa-tools binary (push) Skipped
build-prerelease / Build neuron-blackwell (push) Skipped
build-prerelease / Build neuron-ampere (push) Skipped
build-prerelease / Build neuron-ada (push) Skipped
build-prerelease / Package cortex RPM (push) Skipped
build-prerelease / Package helexa-bench RPM (push) Skipped
build-prerelease / Package helexa-upstream RPM (push) Skipped
build-prerelease / Package helexa-angels RPM (push) Skipped
build-prerelease / Package helexa-router RPM (push) Skipped
build-prerelease / Package helexa-tools RPM (push) Skipped
build-prerelease / Package helexa-neuron-ada RPM (push) Skipped
build-prerelease / Package helexa-neuron-ampere RPM (push) Skipped
build-prerelease / Package helexa-neuron-blackwell RPM (push) Skipped
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Skipped
2026-10-19 12:27:05 +03:00
grenadeandClaude Opus 5.5 4904160431 fix(script): validate scripts wait for in-flight work before evicting
validate-audio.sh and validate-image.sh unloaded the co-resident model
without checking it was idle, killing any request running on it. Wait
for in_flight + queue_depth to reach 0 (WAIT_IDLE_SECS, default 300)
and fail before touching anything if it never does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ohK2pULMVJctL5B1Gixdp
2026-10-19 12:27:05 +03:00
grenade c7fc3308b0 Merge branch 'fix/cortex-glibc-floor': keep cortex installable on Fedora 43
build-prerelease / Resolve version stamps + change detection (push) Successful in 18s
build-prerelease / Build helexa-bench binary (push) Successful in 2m29s
build-prerelease / Build cortex binary (push) Successful in 2m50s
build-prerelease / Build helexa-upstream binary (push) Successful in 2m50s
build-prerelease / Build neuron-ada (push) Successful in 3m3s
build-prerelease / Package helexa-bench RPM (push) Successful in 31s
build-prerelease / Lint (fmt + clippy) (push) Successful in 3m48s
build-prerelease / Build helexa-angels binary (push) Successful in 3m38s
build-prerelease / Build helexa-tools binary (push) Successful in 1m28s
build-prerelease / Package cortex RPM (push) Successful in 54s
build-prerelease / Build helexa-router binary (push) Successful in 1m42s
build-prerelease / Package helexa-upstream RPM (push) Successful in 28s
build-prerelease / Package helexa-tools RPM (push) Successful in 26s
build-prerelease / Package helexa-router RPM (push) Successful in 22s
build-prerelease / Package helexa-angels RPM (push) Successful in 32s
build-prerelease / Test (push) Successful in 13m15s
build-prerelease / Build neuron-blackwell (push) Successful in 15m54s
build-prerelease / Build neuron-ampere (push) Successful in 21m12s
build-prerelease / Package helexa-neuron-ada RPM (push) Successful in 1m16s
build-prerelease / Package helexa-neuron-ampere RPM (push) Successful in 3m57s
build-prerelease / Package helexa-neuron-blackwell RPM (push) Successful in 4m17s
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Successful in 31s
2026-10-19 11:57:26 +03:00
149 changed files with 2212 additions and 366 deletions
+3 -1
View File
@@ -1274,7 +1274,9 @@ stored as that lossless master. MP3 (320 kbps CBR, LAME) is a download
format only: `/content?format=mp3` transcodes the FLAC once and caches
`{id}.mp3` beside it until the job expires; `response_format` sets a
job's default download. Results stored as WAV by older neurons are
served as WAV.
served as WAV. cortex refuses new jobs (503 `audio_storage_full`) while the job
directory's filesystem has under `[audio_jobs] min_free_mb` free (10 GiB
default); shared object storage for the Pascal-scale fleet is #411.
**Metering: per audio-second**, at a per-model rate stored in
helexa-upstream's `model_rates` table (`GET /authz/v1/rates`, cached by
Generated
+1
View File
@@ -875,6 +875,7 @@ dependencies = [
"metrics-exporter-prometheus",
"mp3lame-encoder",
"reqwest",
"rustix",
"serde",
"serde_json",
"tempfile",
+3
View File
@@ -129,3 +129,6 @@ enabled = false
# max_attempts = 2
# How long a job may wait on a busy model before failing, in seconds.
# queue_timeout_secs = 3600
# New jobs are refused (503) while the job directory's filesystem has less
# than this much free space, in MiB. 0 disables the check.
# min_free_mb = 10240
+12
View File
@@ -57,6 +57,12 @@ pub struct AudioJobsConfig {
/// How long a job may wait for a busy model before it fails, in seconds.
#[serde(default = "default_audio_queue_timeout")]
pub queue_timeout_secs: u64,
/// New jobs are refused (503 + `Retry-After`) while the filesystem
/// holding `dir` has less than this much free space, in MiB. Rendered
/// audio is written there, so this keeps a busy store from filling the
/// disk it shares. 0 disables the check.
#[serde(default = "default_audio_min_free_mb")]
pub min_free_mb: u64,
}
impl Default for AudioJobsConfig {
@@ -67,6 +73,7 @@ impl Default for AudioJobsConfig {
max_concurrent: default_audio_max_concurrent(),
max_attempts: default_audio_max_attempts(),
queue_timeout_secs: default_audio_queue_timeout(),
min_free_mb: default_audio_min_free_mb(),
}
}
}
@@ -86,6 +93,11 @@ fn default_audio_max_attempts() -> u32 {
fn default_audio_queue_timeout() -> u64 {
3600
}
/// 10 GiB: room for well over a hundred of the longest songs while jobs
/// already accepted finish.
fn default_audio_min_free_mb() -> u64 {
10 * 1024
}
/// `[upstream]` — the helexa-upstream authority client (#57). Locally
/// unrecognised bearer keys are resolved against `url`'s `/authz/v1` surface
+1
View File
@@ -28,6 +28,7 @@ bytes = "1"
urlencoding = "2"
url = "2"
uuid = { version = "1", features = ["v4"] }
rustix = { version = "1", default-features = false, features = ["std", "fs"] }
claxon = "0.4.3"
mp3lame-encoder = { version = "0.2.5", features = ["std"] }
+47
View File
@@ -55,6 +55,10 @@ const DEFAULT_BUSY_WAIT: Duration = Duration::from_secs(5);
/// How often expired jobs are swept.
const SWEEP_INTERVAL: Duration = Duration::from_secs(600);
/// `Retry-After` on a submission refused for lack of disk space: about as
/// long as it takes the sweep to come round and expire old jobs.
const STORAGE_RETRY_AFTER_SECS: u64 = 600;
/// A job as cortex stores it: the public object plus what is needed to run
/// (or re-run) it.
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -126,6 +130,30 @@ impl AudioJobs {
.as_ref()
}
/// Free space on the filesystem holding the job directory, in MiB.
/// `None` when there is no directory (memory only) or it cannot be
/// read, in which case submissions are not refused on its account.
fn free_mb(&self) -> Option<u64> {
let dir = self.dir()?;
match rustix::fs::statvfs(dir) {
Ok(st) => Some(st.f_bavail.saturating_mul(st.f_frsize) / (1024 * 1024)),
Err(e) => {
tracing::warn!(dir = %dir.display(), error = %e, "cannot read free space for audio jobs");
None
}
}
}
/// Whether a new job may be accepted: the store's filesystem must keep
/// at least `min_free_mb` free. Returns the free space when it may not.
fn storage_full(&self) -> Option<u64> {
let floor = self.config.min_free_mb;
if floor == 0 {
return None;
}
self.free_mb().filter(|free| *free < floor)
}
fn record_path(&self, id: &str) -> Option<PathBuf> {
self.dir().map(|d| d.join(format!("{id}.json")))
}
@@ -342,6 +370,25 @@ async fn submit(
return invalid_params(msg);
}
// Refused before any budget is reserved: nothing to release.
if let Some(free) = fleet.audio_jobs.storage_full() {
metrics::counter!("cortex_audio_jobs_rejected_total", "reason" => "storage").increment(1);
tracing::warn!(
free_mb = free,
min_free_mb = fleet.audio_jobs.config.min_free_mb,
"audio job refused: job store is low on disk space"
);
return crate::error::envelope_response(
OpenAiError::new(
503,
"server_error",
"audio_storage_full",
"audio generation is temporarily unavailable: the job store is full",
)
.with_retry_after(STORAGE_RETRY_AFTER_SECS),
);
}
let owner = crate::metering::principal_from_headers(&headers);
let mut request = request;
request.model = model.clone();
+1
View File
@@ -46,6 +46,7 @@ pub async fn evict_lru_on_node(
.models
.values()
.filter(|m| m.status == ModelStatus::Loaded)
.filter(|m| !fleet.eviction_blocked(node, &m.id))
.filter(|m| match incoming {
Some(inc) => fleet.catalogue.may_displace(inc, &m.id),
None => {
+1 -1
View File
@@ -310,7 +310,7 @@ async fn discover_capabilities(fleet: &CortexState, name: &str, endpoint: &str)
/// Fetch `/health` and stash the activation snapshot on NodeState.
/// Decoupled from the /models poll so a /health glitch doesn't mark
/// the neuron unhealthy or evict the model list.
async fn poll_health(fleet: &CortexState, name: &str, endpoint: &str) {
pub async fn poll_health(fleet: &CortexState, name: &str, endpoint: &str) {
let url = format!("{endpoint}/health");
let resp = match fleet
.http_client
+140 -34
View File
@@ -64,6 +64,10 @@ pub enum RouteError {
"model '{model_id}' is recovering on node '{node}' (device context rebuild in progress) — retry shortly"
)]
ModelRecovering { model_id: String, node: String },
#[error(
"model '{model_id}' does not fit on '{node}' until a model there finishes its work — retry shortly"
)]
NodeBusy { model_id: String, node: String },
}
impl RouteError {
@@ -74,7 +78,8 @@ impl RouteError {
match self {
RouteError::NoHealthyNodes
| RouteError::ModelRecovering { .. }
| RouteError::FeasibleNodeUnhealthy { .. } => 503,
| RouteError::FeasibleNodeUnhealthy { .. }
| RouteError::NodeBusy { .. } => 503,
_ => 404,
}
}
@@ -88,7 +93,8 @@ impl RouteError {
| RouteError::NoFeasibleNeuron { .. }
| RouteError::ColdLoadFailed { .. }
| RouteError::ModelRecovering { .. }
| RouteError::FeasibleNodeUnhealthy { .. } => "api_error",
| RouteError::FeasibleNodeUnhealthy { .. }
| RouteError::NodeBusy { .. } => "api_error",
}
}
@@ -102,6 +108,7 @@ impl RouteError {
RouteError::ColdLoadFailed { .. } => "service_unavailable",
RouteError::ModelRecovering { .. } => "service_unavailable",
RouteError::FeasibleNodeUnhealthy { .. } => "service_unavailable",
RouteError::NodeBusy { .. } => "service_unavailable",
}
}
@@ -115,6 +122,8 @@ impl RouteError {
RouteError::ModelRecovering { .. } => Some(2),
RouteError::FeasibleNodeUnhealthy { .. } => Some(3),
RouteError::NoHealthyNodes => Some(5),
// Clears when the resident model's requests finish.
RouteError::NodeBusy { .. } => Some(10),
_ => None,
}
}
@@ -205,11 +214,10 @@ pub async fn resolve(
// Loading is gateway-synthesised from neuron's
// activation snapshot; it never appears on the
// wire from neuron's `/models`. Skip — the model
// isn't actually servable yet. The pre-existing
// race (catalogue cold_load fires a parallel
// /models/load against the in-flight load) is no
// worse than before; fixing it needs neuron-side
// in-flight tracking on /models/load itself.
// isn't actually servable yet. Cortex's own
// cold-loads are serialised per node (priority 4);
// a load neuron started by itself at activation is
// still invisible to that lock.
ModelStatus::Loading => {}
}
}
@@ -247,41 +255,109 @@ pub async fn resolve(
// Priority 4: catalogue × topology cold-load.
if let Some(profile) = fleet.catalogue.get(model_id) {
let (node_name, neuron_endpoint, fits_free) = pick_feasible_neuron(fleet, profile).await?;
// Cold-swap (#203): when the chosen node's devices don't have the
// free VRAM *right now*, evict unpinned LRU models there until
// the profile fits (bounded — a node whose evictable set can't
// free enough was ranked below one that can).
if !fits_free {
for _ in 0..3 {
match crate::evictor::evict_lru_on_node(fleet, &node_name, Some(&profile.id)).await
{
Ok(Some(evicted)) => {
tracing::info!(
model = %profile.id,
node = %node_name,
evicted = %evicted,
"cold-swap: evicted LRU model to make room"
);
if node_fits_free(fleet, &node_name, profile).await {
break;
}
}
Ok(None) => break,
Err(e) => {
tracing::warn!(node = %node_name, error = %e, "cold-swap eviction failed");
break;
}
let (node_name, neuron_endpoint, _) = pick_feasible_neuron(fleet, profile).await?;
// One cold-load per node at a time. The choice above rests on the
// last poll's VRAM, which cannot see a load in progress, so every
// decision that follows is re-made inside the lock from a fresh
// `/health`.
let lock = fleet.cold_load_lock(&node_name);
let _turn = lock.lock().await;
// Whoever held the lock may have been loading this very model.
if let Some((node, endpoint)) = loaded_location(fleet, model_id).await {
return finish(fleet, &node, &endpoint, model_id, false).await;
}
crate::poller::poll_health(fleet, &node_name, &neuron_endpoint).await;
// Cold-swap (#203): when the node's devices don't have the free
// VRAM right now, evict unpinned LRU models there until the
// profile fits (bounded — a node whose evictable set can't free
// enough was ranked below one that can).
for _ in 0..3 {
if node_fits_free(fleet, &node_name, profile).await {
break;
}
match crate::evictor::evict_lru_on_node(fleet, &node_name, Some(&profile.id)).await {
Ok(Some(evicted)) => {
tracing::info!(
model = %profile.id,
node = %node_name,
evicted = %evicted,
"cold-swap: evicted LRU model to make room"
);
crate::poller::poll_health(fleet, &node_name, &neuron_endpoint).await;
}
Ok(None) => break,
Err(e) => {
tracing::warn!(node = %node_name, error = %e, "cold-swap eviction failed");
break;
}
}
}
// Still short, and a model that would otherwise be displaced is
// busy: loading now would land beside it and fail out of memory.
// Answer "retry shortly" instead; the room appears when its work
// ends.
if !node_fits_free(fleet, &node_name, profile).await
&& blocked_displaceable(fleet, &node_name, profile).await
{
tracing::info!(
model = %profile.id,
node = %node_name,
"cold-load deferred: the model it would displace is busy"
);
return Err(RouteError::NodeBusy {
model_id: profile.id.clone(),
node: node_name,
});
}
cold_load(fleet, &node_name, &neuron_endpoint, profile).await?;
fleet.note_cold_load(&node_name, &profile.id);
// The next cold-load in line decides from these readings.
crate::poller::poll_health(fleet, &node_name, &neuron_endpoint).await;
drop(_turn);
return finish(fleet, &node_name, &neuron_endpoint, model_id, true).await;
}
Err(RouteError::ModelNotFound(model_id.to_string()))
}
/// A healthy node where `model_id` is loaded and servable, if any.
async fn loaded_location(fleet: &Arc<CortexState>, model_id: &str) -> Option<(String, String)> {
let nodes = fleet.nodes.read().await;
nodes
.values()
.filter(|n| n.healthy)
.find(|n| {
n.models.get(model_id).is_some_and(|m| {
matches!(m.status, ModelStatus::Loaded | ModelStatus::Reloading) && m.is_servable()
})
})
.map(|n| (n.name.clone(), n.endpoint.clone()))
}
/// Whether `node_name` holds a model the profile may displace but that
/// cannot be evicted right now because it is busy (see
/// [`CortexState::eviction_blocked`]), or is still loading its startup
/// models.
async fn blocked_displaceable(
fleet: &Arc<CortexState>,
node_name: &str,
profile: &ModelProfile,
) -> bool {
let nodes = fleet.nodes.read().await;
nodes.get(node_name).is_some_and(|node| {
// A startup load cannot be evicted until it finishes, and its
// memory is still arriving: wait for it.
activating(node)
|| node.models.values().any(|m| {
m.status == ModelStatus::Loaded
&& fleet.catalogue.may_displace(&profile.id, &m.id)
&& fleet.eviction_blocked(node, &m.id)
})
})
}
/// True when `node_name`'s live device readings show enough free VRAM
/// on some device for the profile. Conservative: unknown health (no
/// /health poll yet) counts as not-fitting so the ranking prefers
@@ -289,12 +365,40 @@ pub async fn resolve(
async fn node_fits_free(fleet: &Arc<CortexState>, node_name: &str, profile: &ModelProfile) -> bool {
let nodes = fleet.nodes.read().await;
nodes.get(node_name).is_some_and(|node| {
let reserved = activation_reserved_mb(fleet, node);
node.device_health
.iter()
.any(|d| d.vram_free_mb >= profile.vram_mb.unwrap_or(0))
.any(|d| d.vram_free_mb.saturating_sub(reserved) >= profile.vram_mb.unwrap_or(0))
})
}
/// VRAM promised to models the neuron is loading on its own at startup
/// (its `default_models`), by their catalogue footprint. Those loads are
/// not in the neuron's model list until they finish and their memory
/// fills in gradually, so free VRAM alone overstates what is available.
fn activation_reserved_mb(fleet: &CortexState, node: &cortex_core::node::NodeState) -> u64 {
node.activation.as_ref().map_or(0, |a| {
a.in_progress
.iter()
.chain(a.pending.iter())
.filter(|id| {
!node
.models
.get(id.as_str())
.is_some_and(|m| m.status == ModelStatus::Loaded)
})
.filter_map(|id| fleet.catalogue.get(id).and_then(|p| p.vram_mb))
.sum()
})
}
/// Whether the neuron is still loading its startup models.
fn activating(node: &cortex_core::node::NodeState) -> bool {
node.activation
.as_ref()
.is_some_and(|a| a.in_progress.is_some() || !a.pending.is_empty())
}
/// Pick a healthy neuron whose discovered topology satisfies the
/// profile. Preference order (#203):
/// 1. A neuron from `profile.pinned_on` that is healthy + feasible.
@@ -332,7 +436,8 @@ async fn pick_feasible_neuron(
.iter()
.map(|d| d.vram_free_mb)
.max()
.unwrap_or(0);
.unwrap_or(0)
.saturating_sub(activation_reserved_mb(fleet, node));
let need = profile.vram_mb.unwrap_or(0);
let fits_free = max_free >= need;
// Evictable estimate: free + the VRAM of loaded models this
@@ -344,6 +449,7 @@ async fn pick_feasible_neuron(
.values()
.filter(|m| {
matches!(m.status, cortex_core::node::ModelStatus::Loaded)
&& !fleet.eviction_blocked(node, &m.id)
&& fleet.catalogue.may_displace(&profile.id, &m.id)
})
// neuron reports vram_used_mb: null today; fall back to the
+61
View File
@@ -57,8 +57,22 @@ pub struct CortexState {
/// Per-model rates for non-token units (#386), from upstream when it
/// is enabled, else the local default.
pub rates: Arc<crate::rates::Rates>,
/// One lock per node, held across a cold-load's evict → load → fresh
/// `/health` sequence. Placement reads VRAM from the last poll, which
/// cannot see a load still in progress; two cold-loads deciding at
/// once both saw the same free card and loaded side by side into an
/// out-of-memory.
cold_load_locks: std::sync::Mutex<HashMap<String, Arc<tokio::sync::Mutex<()>>>>,
/// When cortex last cold-loaded each `(node, model)`. A model loaded
/// for a request is protected from eviction for [`COLD_LOAD_GRACE`],
/// long enough for that request to reach it; otherwise the next
/// cold-load in line evicts it before it has served anything.
recent_cold_loads: std::sync::Mutex<HashMap<(String, String), std::time::Instant>>,
}
/// How long a freshly cold-loaded model is protected from eviction.
pub const COLD_LOAD_GRACE: std::time::Duration = std::time::Duration::from_secs(30);
impl CortexState {
pub fn from_config(config: &GatewayConfig) -> Self {
let mut nodes = HashMap::new();
@@ -142,6 +156,53 @@ impl CortexState {
} else {
crate::rates::Rates::local()
}),
cold_load_locks: std::sync::Mutex::new(HashMap::new()),
recent_cold_loads: std::sync::Mutex::new(HashMap::new()),
}
}
/// The lock that serialises cold-loads on `node`.
pub fn cold_load_lock(&self, node: &str) -> Arc<tokio::sync::Mutex<()>> {
let mut locks = self
.cold_load_locks
.lock()
.unwrap_or_else(|e| e.into_inner());
Arc::clone(locks.entry(node.to_string()).or_default())
}
/// Record that cortex has just cold-loaded `model` on `node`.
pub fn note_cold_load(&self, node: &str, model: &str) {
let mut recent = self
.recent_cold_loads
.lock()
.unwrap_or_else(|e| e.into_inner());
recent.retain(|_, at| at.elapsed() < COLD_LOAD_GRACE);
recent.insert(
(node.to_string(), model.to_string()),
std::time::Instant::now(),
);
}
/// Whether `model` on `node` is inside its post-cold-load grace.
pub fn recently_cold_loaded(&self, node: &str, model: &str) -> bool {
let recent = self
.recent_cold_loads
.lock()
.unwrap_or_else(|e| e.into_inner());
recent
.get(&(node.to_string(), model.to_string()))
.is_some_and(|at| at.elapsed() < COLD_LOAD_GRACE)
}
/// Whether a resident model must not be evicted right now: it has
/// requests running or queued (as of the last `/health`), or cortex
/// cold-loaded it moments ago for a request that has yet to arrive.
/// Evicting either kills work in progress.
pub fn eviction_blocked(&self, node: &cortex_core::node::NodeState, model: &str) -> bool {
let busy = node
.model_load
.get(model)
.is_some_and(|l| l.in_flight + l.queue_depth > 0);
busy || self.recently_cold_loaded(&node.name, model)
}
}
+43
View File
@@ -202,6 +202,9 @@ fn config(neuron: &str, dir: &std::path::Path) -> GatewayConfig {
audio_jobs: AudioJobsConfig {
dir: dir.to_string_lossy().into_owned(),
queue_timeout_secs: 20,
// The free-space floor is tested on its own below; elsewhere it
// must not make results depend on the build host's disk.
min_free_mb: 0,
..Default::default()
},
}
@@ -741,6 +744,46 @@ async fn expired_jobs_are_swept() {
assert!(!dir.path().join(format!("{id}.mp3")).exists());
}
#[tokio::test]
async fn submissions_are_refused_when_the_store_is_low_on_disk() {
let mock = spawn_mock(Mode::Ok).await;
let rates = [("*", 1000)];
// A floor no filesystem can meet: refused, retryable, nothing written,
// nothing reserved.
let dir = tempfile::tempdir().unwrap();
let mut cfg = config(&mock.url, dir.path());
cfg.audio_jobs.min_free_mb = u64::MAX;
for k in &mut cfg.entitlements.keys {
k.hard_cap = Some(1_000_000);
}
let fleet = Arc::new(CortexState::from_config(&cfg));
let mut table = cortex_gateway::rates::RateTable::default();
for (model, rate) in rates {
table.set(cortex_gateway::rates::AUDIO_SECOND, model, rate);
}
fleet.rates.replace(table).await;
seed_model(&fleet).await;
let gw = serve(&fleet).await;
let resp = submit(&gw, Some("key-alice"), song()).await;
assert_eq!(resp.status(), 503);
assert_eq!(resp.headers()["retry-after"], "600");
let body: Value = resp.json().await.unwrap();
assert_eq!(body["error"]["code"], "audio_storage_full");
assert_eq!(spent(&fleet, "alice").await, 0);
assert_eq!(std::fs::read_dir(dir.path()).unwrap().count(), 0);
// A floor any filesystem meets: accepted.
let dir = tempfile::tempdir().unwrap();
let mut cfg = config(&mock.url, dir.path());
cfg.audio_jobs.min_free_mb = 1;
let fleet = Arc::new(CortexState::from_config(&cfg));
seed_model(&fleet).await;
let gw = serve(&fleet).await;
assert_eq!(submit(&gw, None, song()).await.status(), 202);
}
// ── Per-model rates (#386) ───────────────────────────────────────────
/// A gateway whose keys are capped at `cap` tokens and whose audio rate
+308
View File
@@ -0,0 +1,308 @@
//! Cold-load placement under concurrency.
//!
//! The mock neuron keeps real VRAM books for one 24 GB card: loads take
//! time, `/health` reports what is actually resident, and the card notes
//! if it is ever asked to hold more than it has. That last flag is the
//! failure being guarded against — two cold-loads deciding from the same
//! stale poll, both landing, and every request on the card then failing
//! out of memory.
use axum::extract::{Path, State};
use axum::routing::{get, post};
use axum::{Json, Router};
use cortex_core::config::{
EvictionSettings, EvictionStrategy, GatewayConfig, GatewaySettings, NeuronEndpoint,
};
use cortex_core::discovery::{DeviceInfo, DiscoveryResponse};
use cortex_core::node::{ModelEntry, ModelStatus};
use cortex_gateway::router::RouteError;
use cortex_gateway::state::CortexState;
use serde_json::{Value, json};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::Duration;
const CARD_MB: u64 = 24_000;
const CATALOGUE: &str = r#"
[[models]]
id = "music-a"
harness = "candle"
vram_mb = 14000
[[models]]
id = "music-b"
harness = "candle"
vram_mb = 12000
"#;
#[derive(Default)]
struct Card {
resident: HashMap<String, u64>,
in_flight: HashMap<String, usize>,
overcommitted: bool,
unloads: Vec<String>,
/// A model the neuron is loading by itself at startup: not resident
/// yet, its memory not yet showing.
activating: Option<String>,
loads: Vec<String>,
}
fn footprint(model: &str) -> u64 {
match model {
"music-a" => 14_000,
"music-b" => 12_000,
_ => 1_000,
}
}
async fn spawn_neuron(card: Arc<Mutex<Card>>) -> String {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let url = format!("http://{}", listener.local_addr().unwrap());
let inference = url.clone();
let app = Router::new()
.route(
"/models/load",
post(
|State(card): State<Arc<Mutex<Card>>>, Json(spec): Json<Value>| async move {
let id = spec["model_id"].as_str().unwrap().to_string();
// A load takes a while: long enough for a second
// request to decide while this one is in progress.
tokio::time::sleep(Duration::from_millis(300)).await;
let mut card = card.lock().unwrap();
card.loads.push(id.clone());
card.resident.insert(id.clone(), footprint(&id));
if card.resident.values().sum::<u64>() > CARD_MB {
card.overcommitted = true;
}
Json(json!({"status": "loaded"}))
},
),
)
.route(
"/models/unload",
post(
|State(card): State<Arc<Mutex<Card>>>, Json(body): Json<Value>| async move {
let id = body["model_id"].as_str().unwrap().to_string();
let mut card = card.lock().unwrap();
card.resident.remove(&id);
card.unloads.push(id);
Json(json!({"status": "unloaded"}))
},
),
)
.route(
"/health",
get(|State(card): State<Arc<Mutex<Card>>>| async move {
let card = card.lock().unwrap();
let used: u64 = card.resident.values().sum();
let models: Vec<Value> = card
.resident
.keys()
.map(|id| {
json!({
"id": id,
"in_flight": card.in_flight.get(id).copied().unwrap_or(0),
"queue_depth": 0
})
})
.collect();
let activation = match &card.activating {
Some(id) => json!({"state": "pre_warming", "in_progress": id}),
None => json!({"state": "ready"}),
};
Json(json!({
"uptime_secs": 1,
"activation": activation,
"devices": [{
"index": 0,
"vram_used_mb": used,
"vram_free_mb": CARD_MB.saturating_sub(used),
"utilization_pct": 0,
"temp_c": 40
}],
"models": models
}))
}),
)
.route(
"/models/{id}/endpoint",
get(move |Path(_id): Path<String>| {
let url = inference.clone();
async move { Json(json!({"url": url})) }
}),
)
.with_state(card);
tokio::spawn(async move { axum::serve(listener, app).await.unwrap() });
url
}
/// A gateway with one healthy 24 GB node, its first `/health` already in.
async fn gateway(neuron: &str, tag: &str) -> Arc<CortexState> {
let path = std::env::temp_dir().join(format!("cortex-cold-load-{tag}.toml"));
std::fs::write(&path, CATALOGUE).unwrap();
let config = GatewayConfig {
gateway: GatewaySettings {
listen: "127.0.0.1:0".into(),
metrics_listen: "127.0.0.1:0".into(),
},
eviction: EvictionSettings {
strategy: EvictionStrategy::Lru,
defrag_after_cycles: 0,
},
neurons: vec![NeuronEndpoint {
name: "gpu".into(),
endpoint: neuron.to_string(),
}],
models_config: path.to_string_lossy().into_owned(),
entitlements: Default::default(),
upstream: Default::default(),
audio_jobs: Default::default(),
};
let fleet = Arc::new(CortexState::from_config(&config));
{
let mut nodes = fleet.nodes.write().await;
let node = nodes.get_mut("gpu").unwrap();
node.healthy = true;
node.discovery = Some(DiscoveryResponse {
hostname: "gpu".into(),
os: "linux".into(),
kernel: "6".into(),
cuda_version: None,
driver_version: None,
devices: vec![DeviceInfo {
index: 0,
name: "RTX 4090".into(),
vram_total_mb: CARD_MB,
compute_capability: "8.9".into(),
}],
harnesses: vec!["candle".into()],
cuda_unavailable_reason: None,
max_prompt_tokens: 0,
});
}
cortex_gateway::poller::poll_health(&fleet, "gpu", neuron).await;
fleet
}
/// Mark `id` resident in the mock and in cortex's view, last used long
/// ago — as the poller would have seen it, not as a fresh cold-load.
async fn seed_resident(fleet: &CortexState, card: &Mutex<Card>, neuron: &str, id: &str) {
card.lock()
.unwrap()
.resident
.insert(id.into(), footprint(id));
{
let mut nodes = fleet.nodes.write().await;
nodes.get_mut("gpu").unwrap().models.insert(
id.into(),
ModelEntry {
id: id.into(),
status: ModelStatus::Loaded,
last_accessed: Some(chrono::Utc::now() - chrono::Duration::minutes(10)),
vram_estimate_mb: Some(footprint(id)),
capabilities: Vec::new(),
tool_call: false,
reasoning: false,
limit: None,
servable: None,
reasoning_budget: Vec::new(),
},
);
}
cortex_gateway::poller::poll_health(fleet, "gpu", neuron).await;
}
#[tokio::test]
async fn concurrent_cold_loads_never_overcommit_the_card() {
let card = Arc::new(Mutex::new(Card::default()));
let neuron = spawn_neuron(Arc::clone(&card)).await;
let fleet = gateway(&neuron, "concurrent").await;
// Both decide while the card is empty; together they do not fit.
let (a, b) = tokio::join!(cortex_gateway::router::resolve(&fleet, "music-a"), async {
tokio::time::sleep(Duration::from_millis(50)).await;
cortex_gateway::router::resolve(&fleet, "music-b").await
});
let card = card.lock().unwrap();
assert!(!card.overcommitted, "both models were resident at once");
assert!(a.is_ok(), "{a:?}");
// music-a was cold-loaded for a request that has yet to reach it, so
// music-b may not take its place yet: retry shortly, not a failure.
match b {
Err(e @ RouteError::NodeBusy { .. }) => {
assert_eq!(e.http_status(), 503);
assert!(e.retry_after_secs().is_some());
}
other => panic!("expected NodeBusy, got {other:?}"),
}
assert_eq!(card.resident.keys().collect::<Vec<_>>(), ["music-a"]);
assert!(card.unloads.is_empty());
}
#[tokio::test]
async fn a_model_with_requests_in_flight_is_not_evicted() {
let card = Arc::new(Mutex::new(Card::default()));
let neuron = spawn_neuron(Arc::clone(&card)).await;
let fleet = gateway(&neuron, "in-flight").await;
card.lock().unwrap().in_flight.insert("music-a".into(), 1);
seed_resident(&fleet, &card, &neuron, "music-a").await;
let b = cortex_gateway::router::resolve(&fleet, "music-b").await;
assert!(matches!(b, Err(RouteError::NodeBusy { .. })), "{b:?}");
let card = card.lock().unwrap();
assert!(card.unloads.is_empty(), "evicted a busy model");
assert!(!card.overcommitted);
}
#[tokio::test]
async fn an_idle_model_is_swapped_out() {
let card = Arc::new(Mutex::new(Card::default()));
let neuron = spawn_neuron(Arc::clone(&card)).await;
let fleet = gateway(&neuron, "idle").await;
seed_resident(&fleet, &card, &neuron, "music-a").await;
let b = cortex_gateway::router::resolve(&fleet, "music-b").await;
assert!(b.is_ok(), "{b:?}");
let card = card.lock().unwrap();
assert_eq!(card.unloads, ["music-a"]);
assert_eq!(card.resident.keys().collect::<Vec<_>>(), ["music-b"]);
assert!(!card.overcommitted);
}
#[tokio::test]
async fn a_cold_load_waits_for_the_neuron_s_own_startup_load() {
// The neuron has just restarted and is loading its default model.
// That load is in no model list yet and its memory has barely
// started to fill, so the card still looks empty.
let card = Arc::new(Mutex::new(Card {
activating: Some("music-a".into()),
..Default::default()
}));
let neuron = spawn_neuron(Arc::clone(&card)).await;
let fleet = gateway(&neuron, "activating").await;
let b = cortex_gateway::router::resolve(&fleet, "music-b").await;
assert!(matches!(b, Err(RouteError::NodeBusy { .. })), "{b:?}");
assert!(
card.lock().unwrap().loads.is_empty(),
"loaded over a startup load"
);
// Once the startup load finishes and its model is idle, the swap
// goes ahead as usual.
{
let mut c = card.lock().unwrap();
c.activating = None;
}
seed_resident(&fleet, &card, &neuron, "music-a").await;
let b = cortex_gateway::router::resolve(&fleet, "music-b").await;
assert!(b.is_ok(), "{b:?}");
let card = card.lock().unwrap();
assert_eq!(card.unloads, ["music-a"]);
assert!(!card.overcommitted);
}
@@ -1,7 +1,7 @@
---
title: Using decisions
sidebar_label: Decisions
description: Typed questions over text or JSON, answered with calibrated probabilities — the System-One endpoint and its playground.
description: Typed questions over text or JSON, answered with calibrated probabilities — the System-One endpoint and the Decisions page.
---
# Using decisions
@@ -13,7 +13,7 @@ request. That makes it fast (tens of milliseconds), cheap, and
predictable enough to put in front of a workflow: routing a ticket,
screening a message, deciding whether to escalate.
Try it without writing code in [the playground](/playground).
Try it without writing code on [the Decisions page](/decisions).
The endpoint is `POST https://helexa.ai/v1/systemone`, and it speaks the
TypeSafe Jev protocol, so existing Jev clients work unchanged.
+304 -16
View File
@@ -1888,53 +1888,209 @@ html[dir="rtl"] .hx-landing-composer textarea {
/* -- Music (/music) -------------------------------------------------------- */
/* The music page reuses the image form controls; these cover what images
* has no counterpart for: labelled text areas and a job list. */
* has no counterpart for. */
.hx-music-field {
display: flex;
flex-direction: column;
gap: 0.25rem;
gap: 0.35rem;
min-width: 0;
font-size: 0.82rem;
color: var(--color-text-muted);
}
.hx-music-field textarea,
.hx-music input[type="number"] {
.hx-music-field textarea {
border-radius: 0.5rem;
border: 1px solid var(--color-border-subtle);
background-color: var(--color-bg-elevated);
color: var(--color-text);
padding: 0.45rem 0.6rem;
padding: 0.55rem 0.7rem;
font: inherit;
font-size: 0.9rem;
font-size: 0.92rem;
line-height: 1.5;
min-height: 36px;
resize: vertical;
}
.hx-music input[type="number"] {
width: 7rem;
/* Style beside lyrics on a wide screen: lyrics are short lines, so a
* full-width box wastes most of its width. Both boxes share one height. */
.hx-music-words {
display: grid;
gap: 1rem;
}
.hx-music-jobs {
@media (min-width: 768px) {
.hx-music-words.two {
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
.hx-music-words.two .hx-music-field textarea {
flex: 1 1 auto;
}
}
.hx-music-tags {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
}
.hx-music-tag {
padding: 0.2rem 0.65rem;
border: 1px solid var(--color-border-subtle);
border-radius: 999px;
background: none;
color: var(--color-text-muted);
font-size: 0.78rem;
transition:
border-color var(--transition-fast),
color var(--transition-fast),
background-color var(--transition-fast);
}
.hx-music-tag:hover:not(:disabled) {
border-color: var(--color-accent);
color: var(--color-text);
}
.hx-music-tag.on {
border-color: var(--color-accent);
background-color: var(--color-accent-soft);
color: var(--color-text);
}
.hx-music-length {
flex: 1 1 14rem;
max-width: 22rem;
}
.hx-music-length strong {
color: var(--color-text);
font-variant-numeric: tabular-nums;
}
.hx-music-length input[type="range"] {
width: 100%;
accent-color: var(--color-accent);
}
/* A disabled generate button must read as unavailable at a glance, not
* as a slightly faded gradient. */
.hx-music .hx-music-generate:disabled {
background: var(--color-bg-subtle);
color: var(--color-text-muted);
box-shadow: inset 0 0 0 1px var(--color-border-subtle);
opacity: 1;
cursor: not-allowed;
}
/* Phone widths: the history button sits over the title's start. */
@media (max-width: 768px) {
.hx-music > h1 {
padding-inline-start: 2.75rem;
}
}
.hx-music-blocker {
margin: -0.4rem 0 0;
text-align: end;
color: var(--color-text-muted);
}
.hx-music-spin {
animation: hx-music-turn 0.9s linear infinite;
}
@keyframes hx-music-turn {
to {
transform: rotate(360deg);
}
}
/* The studio: what is happening now, below the form. Empty on arrival. */
.hx-music-studio {
margin-top: 1.5rem;
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.hx-music-job {
padding: 0.7rem 0.8rem;
border: 1px solid var(--color-border-subtle);
.hx-music-studio:empty {
display: none;
}
.hx-music-activity {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.8rem 1rem;
border: 1px dashed var(--color-border-strong);
border-radius: 0.75rem;
color: var(--color-text-muted);
font-size: 0.9rem;
}
.hx-music-job {
display: grid;
grid-template-columns: 5.5rem minmax(0, 1fr);
gap: 1rem;
padding: 0.9rem;
border: 1px solid var(--color-border-subtle);
border-radius: 0.9rem;
background-color: var(--color-bg-elevated);
}
.hx-music-cover {
width: 5.5rem;
aspect-ratio: 1;
border-radius: 0.6rem;
display: grid;
place-items: center;
position: relative;
overflow: hidden;
box-shadow: 0 6px 18px rgb(0 0 0 / 0.25);
}
/* A record's label and groove sheen over the gradient. */
.hx-music-cover::after {
content: "";
position: absolute;
inset: 18%;
border-radius: 50%;
background: radial-gradient(circle, rgb(255 255 255 / 0.35) 0 12%, transparent 13% 100%),
repeating-radial-gradient(circle, rgb(0 0 0 / 0.12) 0 1px, transparent 1px 4px);
mix-blend-mode: overlay;
}
.hx-music-cover .hx-music-eq {
position: relative;
z-index: 1;
color: #fff;
height: 1.6rem;
}
.hx-music-job-body {
display: flex;
flex-direction: column;
gap: 0.4rem;
min-width: 0;
}
.hx-music-job header {
display: flex;
align-items: flex-start;
gap: 0.5rem;
}
.hx-music-title {
margin: 0;
flex: 1 1 auto;
min-width: 0;
font-size: 1rem;
font-weight: 600;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.hx-music-close {
flex-shrink: 0;
margin-block-start: -0.25rem;
}
.hx-music-prompt {
margin: 0;
font-size: 0.85rem;
color: var(--color-text-muted);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.hx-music-meta {
display: flex;
flex-wrap: wrap;
gap: 0.6rem;
align-items: baseline;
gap: 0.25rem 0.75rem;
font-size: 0.8rem;
color: var(--color-text-muted);
}
.hx-music-status {
font-size: 0.8rem;
font-weight: 600;
color: var(--color-text);
}
.hx-music-running .hx-music-status,
.hx-music-queued .hx-music-status {
@@ -1951,9 +2107,135 @@ html[dir="rtl"] .hx-landing-composer textarea {
.hx-music-job .hx-image-card-actions {
flex-wrap: wrap;
}
@media (max-width: 575.98px) {
.hx-music-job {
grid-template-columns: 3.5rem minmax(0, 1fr);
gap: 0.75rem;
}
.hx-music-cover {
width: 3.5rem;
}
}
/* Equaliser bars: the "something is being made" signal. */
.hx-music-eq {
display: inline-flex;
align-items: flex-end;
gap: 3px;
height: 1.1rem;
color: var(--color-accent);
}
.hx-music-eq i {
width: 3px;
height: 100%;
border-radius: 2px;
background: currentColor;
transform-origin: bottom;
animation: hx-music-bounce 0.9s ease-in-out infinite;
}
.hx-music-eq i:nth-child(2) {
animation-delay: -0.3s;
}
.hx-music-eq i:nth-child(3) {
animation-delay: -0.6s;
}
.hx-music-eq i:nth-child(4) {
animation-delay: -0.15s;
}
@keyframes hx-music-bounce {
0%,
100% {
transform: scaleY(0.25);
}
50% {
transform: scaleY(1);
}
}
/* The player: a round play button, a waveform that is the seek bar, the
* time. */
.hx-music-player {
width: min(100%, 24rem);
height: 36px;
display: flex;
align-items: center;
gap: 0.75rem;
margin-block: 0.25rem;
}
.hx-music-play {
flex-shrink: 0;
width: 2.6rem;
height: 2.6rem;
border: none;
border-radius: 50%;
display: grid;
place-items: center;
background: var(--gradient-helix);
color: #fff;
transition: transform var(--transition-fast);
}
.hx-music-play:hover:not(:disabled) {
transform: scale(1.06);
}
html[dir="rtl"] .hx-music-play svg {
transform: scaleX(-1);
}
.hx-music-wave {
flex: 1 1 auto;
min-width: 0;
height: 2.6rem;
display: flex;
align-items: center;
gap: 2px;
cursor: pointer;
}
.hx-music-wave i {
flex: 1 1 0;
min-width: 0;
border-radius: 1px;
background-color: var(--color-border-strong);
transition: background-color 80ms linear;
}
.hx-music-wave i.on {
background-color: var(--color-accent);
}
.hx-music-player.standin .hx-music-wave i {
opacity: 0.55;
}
.hx-music-wave:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 3px;
border-radius: 4px;
}
.hx-music-time {
flex-shrink: 0;
font-size: 0.78rem;
color: var(--color-text-muted);
font-variant-numeric: tabular-nums;
}
.hx-music-spinner {
width: 1rem;
height: 1rem;
border-radius: 50%;
border: 2px solid rgb(255 255 255 / 0.4);
border-top-color: #fff;
animation: hx-music-turn 0.8s linear infinite;
}
@media (max-width: 575.98px) {
.hx-music-time {
display: none;
}
.hx-music-wave {
gap: 1px;
}
}
@media (prefers-reduced-motion: reduce) {
.hx-music-eq i,
.hx-music-spin,
.hx-music-spinner {
animation: none;
}
.hx-music-eq i {
transform: scaleY(0.6);
}
}
/* History sidebar (#402): the chat sidebar, with collapsible sections. */
@@ -2009,6 +2291,12 @@ html[dir="rtl"] .hx-flip-rtl {
.hx-music-dot-failed {
background-color: var(--color-accent-hot);
}
.hx-music-swatch {
flex-shrink: 0;
width: 0.9rem;
height: 0.9rem;
border-radius: 0.2rem;
}
@keyframes hx-music-pulse {
50% {
opacity: 0.25;
+1 -1
View File
@@ -43,7 +43,7 @@ export default function App() {
<Route path="/chat" element={<Chat />} />
<Route path="/images" element={<Images />} />
<Route path="/music" element={<Music />} />
<Route path="/playground" element={<Playground />} />
<Route path="/decisions" element={<Playground />} />
{/* Documentation. The splat route lets the sidebar tree be
arbitrarily deep without enumerating routes here — the
page resolves the slug against the content tree. */}
+2 -2
View File
@@ -115,12 +115,12 @@ const Header: React.FC = () => {
{t("nav.music")}
</NavLink>
<NavLink
to="/playground"
to="/decisions"
className={({ isActive }): string =>
isActive ? "nav-link active" : "nav-link"
}
>
{t("nav.playground")}
{t("nav.decisions")}
</NavLink>
{/* Documentation is reachable on every host — this only
decides whether it is advertised. The pages are still
+3 -3
View File
@@ -64,9 +64,9 @@ describe("decisions translations", () => {
}
});
it.each(Object.keys(COMMON))("%s names the playground in the nav", (lang) => {
it.each(Object.keys(COMMON))("%s names the decisions page in the nav", (lang) => {
const nav = COMMON[lang].nav as Tree;
expect(typeof nav.playground).toBe("string");
expect((nav.playground as string).trim()).not.toBe("");
expect(typeof nav.decisions).toBe("string");
expect((nav.decisions as string).trim()).not.toBe("");
});
});
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "ውይይት",
"images": "ምስሎች",
"music": "ሙዚቃ",
"playground": "የሙከራ ሜዳ",
"decisions": "ውሳኔዎች",
"mission": "ተልእኮ",
"login": "ይግቡ",
"register": "ይመዝገቡ",
@@ -1,7 +1,7 @@
{
"page": {
"title": "የሙከራ ሜዳ",
"lead": "ለውሳኔ ሞዴል ስለማንኛውም ጽሑፍ ወይም JSON ዓይነት ያላቸው ጥያቄዎችን ይጠይቁ፤ በአንድ ዙር የተስተካከሉ መልሶችን ያግኙ — ያለ የመነጨ ጽሑፍ።",
"title": "ውሳኔዎች",
"lead": "ስለማንኛውም ጽሑፍ ወይም JSON አዎ/አይደለም፣ ባለብዙ ምርጫ ወይም የነጥብ ጥያቄዎችን ይጠይቁ፤ በተስተካከለ እርግጠኝነት ፈጣን መልሶችን ያግኙ። እንደዚህ ያሉ ሞዴሎች አንዳንድ ጊዜ System One ሞዴሎች ይባላሉ።",
"docsLink": "እንዴት እንደሚሠራ",
"side": "ውጤቶች፣ ምሳሌዎች እና ኮድ",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "ያለፉት 7 ቀናት",
"month": "ያለፉት 30 ቀናት",
"older": "የቆዩ"
}
},
"length": "ርዝመት",
"tags": "የስልት ሃሳቦች",
"submitting": "ዘፈንዎ ወደ ስቱዲዮ እየተላከ ነው…",
"needStyle": "ዘፈን ለመፍጠር ስልት ይግለጹ።",
"needLength": "ከ{{min}} እስከ {{max}} ሰከንድ ያለ ርዝመት ይምረጡ።",
"close": "ዝጋ",
"pause": "ለአፍታ አቁም",
"seek": "በዘፈኑ ውስጥ ያለ ቦታ"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "المحادثة",
"images": "الصور",
"music": "الموسيقى",
"playground": "ساحة التجربة",
"decisions": "القرارات",
"mission": "المهمة",
"login": "تسجيل الدخول",
"register": "إنشاء حساب",
@@ -1,7 +1,7 @@
{
"page": {
"title": "ساحة التجربة",
"lead": "اطرح على نموذج قرارات أسئلة محدّدة النوع عن أي نص أو JSON، واحصل على إجابات معايَرة في تمريرة واحدة — دون نص مولَّد.",
"title": "القرارات",
"lead": "اطرح أسئلة بنعم/لا أو اختيار من متعدد أو تقييم حول أي نص أو JSON، واحصل فورًا على إجابات بثقة معايَرة. تُسمّى هذه النماذج أحيانًا نماذج System One.",
"docsLink": "كيف يعمل",
"side": "النتائج والأمثلة والشيفرة",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "آخر 7 أيام",
"month": "آخر 30 يومًا",
"older": "أقدم"
}
},
"length": "المدة",
"tags": "أفكار للأسلوب",
"submitting": "جارٍ إرسال أغنيتك إلى الاستوديو…",
"needStyle": "صِف أسلوبًا لإنشاء أغنية.",
"needLength": "اختر مدة بين {{min}} و{{max}} ثانية.",
"close": "إغلاق",
"pause": "إيقاف مؤقت",
"seek": "الموضع في الأغنية"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Чат",
"images": "Изображения",
"music": "Музика",
"playground": "Пясъчник",
"decisions": "Решения",
"mission": "Мисия",
"login": "Вход",
"register": "Регистрация",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Пясъчник",
"lead": "Задавайте на модел за вземане на решения типизирани въпроси за всякакъв текст или JSON и получавайте калибрирани отговори с едно преминаване — без генериран текст.",
"title": "Решения",
"lead": "Задавайте въпроси с да/не, с избор между възможности или с оценка за всякакъв текст или JSON и получавайте мигновено отговори с калибрирана увереност. Такива модели понякога се наричат модели System One.",
"docsLink": "Как работи",
"side": "Резултати, примери и код",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Последните 7 дни",
"month": "Последните 30 дни",
"older": "По-стари"
}
},
"length": "Продължителност",
"tags": "Идеи за стил",
"submitting": "Изпращане на песента ви към студиото…",
"needStyle": "Опишете стил, за да генерирате песен.",
"needLength": "Изберете продължителност между {{min}} и {{max}} секунди.",
"close": "Затвори",
"pause": "Пауза",
"seek": "Позиция в песента"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Xat",
"images": "Imatges",
"music": "Música",
"playground": "Espai de proves",
"decisions": "Decisions",
"mission": "Missió",
"login": "Inicia la sessió",
"register": "Registra't",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Espai de proves",
"lead": "Feu a un model de decisió preguntes tipades sobre qualsevol text o JSON i obtingueu respostes calibrades en una sola passada, sense text generat.",
"title": "Decisions",
"lead": "Feu preguntes de sí/no, d’opció múltiple o de puntuació sobre qualsevol text o JSON i obteniu a l’instant respostes amb una confiança calibrada. De vegades se’n diu models System One.",
"docsLink": "Com funciona",
"side": "Resultats, exemples i codi",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Darrers 7 dies",
"month": "Darrers 30 dies",
"older": "Més antics"
}
},
"length": "Durada",
"tags": "Idees d’estil",
"submitting": "S’està enviant la cançó a l’estudi…",
"needStyle": "Descriviu un estil per generar una cançó.",
"needLength": "Trieu una durada entre {{min}} i {{max}} segons.",
"close": "Tanca",
"pause": "Pausa",
"seek": "Posició a la cançó"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Chat",
"images": "Obrázky",
"music": "Hudba",
"playground": "Pískoviště",
"decisions": "Rozhodnutí",
"mission": "Mise",
"login": "Přihlásit se",
"register": "Registrovat se",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Pískoviště",
"lead": "Pokládejte rozhodovacímu modelu typované otázky o libovolném textu nebo JSON a dostávejte kalibrované odpovědi v jediném průchodu — bez generovaného textu.",
"title": "Rozhodnutí",
"lead": "Ptejte se otázkami ano/ne, s výběrem z možností nebo s hodnocením na libovolný text či JSON a okamžitě získejte odpovědi s kalibrovanou jistotou. Takovým modelům se někdy říká modely System One.",
"docsLink": "Jak to funguje",
"side": "Výsledky, příklady a kód",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Posledních 7 dní",
"month": "Posledních 30 dní",
"older": "Starší"
}
},
"length": "Délka",
"tags": "Nápady na styl",
"submitting": "Odesílám skladbu do studia…",
"needStyle": "Popište styl, abyste mohli vygenerovat skladbu.",
"needLength": "Zvolte délku mezi {{min}} a {{max}} sekundami.",
"close": "Zavřít",
"pause": "Pozastavit",
"seek": "Pozice ve skladbě"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Chat",
"images": "Billeder",
"music": "Musik",
"playground": "Legeplads",
"decisions": "Beslutninger",
"mission": "Mission",
"login": "Log ind",
"register": "Opret konto",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Legeplads",
"lead": "Stil en beslutningsmodel typede spørgsmål om enhver tekst eller JSON, og få kalibrerede svar i ét gennemløb — uden genereret tekst.",
"title": "Beslutninger",
"lead": "Stil ja/nej-, multiple choice- eller pointspørgsmål om enhver tekst eller JSON, og få straks svar med kalibreret sikkerhed. Den slags modeller kaldes nogle gange System One-modeller.",
"docsLink": "Sådan virker det",
"side": "Resultater, eksempler og kode",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Seneste 7 dage",
"month": "Seneste 30 dage",
"older": "Ældre"
}
},
"length": "Længde",
"tags": "Stilidéer",
"submitting": "Sender din sang til studiet…",
"needStyle": "Beskriv en stil for at generere en sang.",
"needLength": "Vælg en længde mellem {{min}} og {{max}} sekunder.",
"close": "Luk",
"pause": "Pause",
"seek": "Position i sangen"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Chat",
"images": "Bilder",
"music": "Musik",
"playground": "Playground",
"decisions": "Entscheidungen",
"mission": "Mission",
"login": "Anmelden",
"register": "Registrieren",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Playground",
"lead": "Stellen Sie einem Entscheidungsmodell typisierte Fragen zu beliebigem Text oder JSON und erhalten Sie kalibrierte Antworten in einem Durchlauf – ganz ohne generierten Text.",
"title": "Entscheidungen",
"lead": "Stellen Sie Ja/Nein-, Auswahl- oder Bewertungsfragen zu beliebigem Text oder JSON und erhalten Sie sofort Antworten mit kalibrierter Konfidenz. Solche Modelle werden manchmal System-One-Modelle genannt.",
"docsLink": "So funktioniert es",
"side": "Ergebnisse, Beispiele und Code",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Letzte 7 Tage",
"month": "Letzte 30 Tage",
"older": "Älter"
}
},
"length": "Länge",
"tags": "Stilideen",
"submitting": "Ihr Song wird ans Studio gesendet…",
"needStyle": "Beschreiben Sie einen Stil, um einen Song zu erzeugen.",
"needLength": "Wählen Sie eine Länge zwischen {{min}} und {{max}} Sekunden.",
"close": "Schließen",
"pause": "Pause",
"seek": "Position im Song"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Συνομιλία",
"images": "Εικόνες",
"music": "Μουσική",
"playground": "Πεδίο δοκιμών",
"decisions": "Αποφάσεις",
"mission": "Αποστολή",
"login": "Σύνδεση",
"register": "Εγγραφή",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Πεδίο δοκιμών",
"lead": "Κάντε σε ένα μοντέλο αποφάσεων τυποποιημένες ερωτήσεις για οποιοδήποτε κείμενο ή JSON και λάβετε βαθμονομημένες απαντήσεις σε ένα μόνο πέρασμα — χωρίς παραγόμενο κείμενο.",
"title": "Αποφάσεις",
"lead": "Κάντε ερωτήσεις ναι/όχι, πολλαπλής επιλογής ή βαθμολόγησης για οποιοδήποτε κείμενο ή JSON και λάβετε αμέσως απαντήσεις με βαθμονομημένη βεβαιότητα. Τέτοια μοντέλα ονομάζονται μερικές φορές μοντέλα System One.",
"docsLink": "Πώς λειτουργεί",
"side": "Αποτελέσματα, παραδείγματα και κώδικας",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Τελευταίες 7 ημέρες",
"month": "Τελευταίες 30 ημέρες",
"older": "Παλαιότερα"
}
},
"length": "Διάρκεια",
"tags": "Ιδέες ύφους",
"submitting": "Αποστολή του τραγουδιού σας στο στούντιο…",
"needStyle": "Περιγράψτε ένα ύφος για να δημιουργήσετε τραγούδι.",
"needLength": "Επιλέξτε διάρκεια από {{min}} έως {{max}} δευτερόλεπτα.",
"close": "Κλείσιμο",
"pause": "Παύση",
"seek": "Θέση στο τραγούδι"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Chat",
"images": "Images",
"music": "Music",
"playground": "Playground",
"decisions": "Decisions",
"mission": "Mission",
"login": "Sign in",
"register": "Sign up",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Playground",
"lead": "Ask a decision model typed questions about any text or JSON, and get calibrated answers in one pass — no generated text.",
"title": "Decisions",
"lead": "Ask yes/no, multiple-choice or scoring questions about any text or JSON, and get instant answers with calibrated confidence. Models like this are sometimes called System One models.",
"docsLink": "How it works",
"side": "Results, examples and code",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Previous 7 days",
"month": "Previous 30 days",
"older": "Older"
}
},
"length": "Length",
"tags": "Style ideas",
"submitting": "Sending your song to the studio…",
"needStyle": "Describe a style to generate a song.",
"needLength": "Choose a length between {{min}} and {{max}} seconds.",
"close": "Close",
"pause": "Pause",
"seek": "Position in the song"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Chat",
"images": "Imágenes",
"music": "Música",
"playground": "Zona de pruebas",
"decisions": "Decisiones",
"mission": "Misión",
"login": "Iniciar sesión",
"register": "Registrarse",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Zona de pruebas",
"lead": "Haga a un modelo de decisión preguntas tipadas sobre cualquier texto o JSON y obtenga respuestas calibradas en una sola pasada, sin texto generado.",
"title": "Decisiones",
"lead": "Haga preguntas de sí/no, de opción múltiple o de puntuación sobre cualquier texto o JSON y obtenga al instante respuestas con una confianza calibrada. A estos modelos a veces se les llama modelos System One.",
"docsLink": "Cómo funciona",
"side": "Resultados, ejemplos y código",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Últimos 7 días",
"month": "Últimos 30 días",
"older": "Anteriores"
}
},
"length": "Duración",
"tags": "Ideas de estilo",
"submitting": "Enviando su canción al estudio…",
"needStyle": "Describa un estilo para generar una canción.",
"needLength": "Elija una duración entre {{min}} y {{max}} segundos.",
"close": "Cerrar",
"pause": "Pausa",
"seek": "Posición en la canción"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Vestlus",
"images": "Pildid",
"music": "Muusika",
"playground": "Mänguväljak",
"decisions": "Otsused",
"mission": "Missioon",
"login": "Logi sisse",
"register": "Registreeru",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Mänguväljak",
"lead": "Esitage otsustusmudelile tüübitud küsimusi mis tahes teksti või JSON-i kohta ja saage kalibreeritud vastused ühe läbimisega — ilma genereeritud tekstita.",
"title": "Otsused",
"lead": "Esita jah/ei-, valikvastustega või hindamisküsimusi mis tahes teksti või JSON-i kohta ja saa kohe vastused kalibreeritud kindlusega. Selliseid mudeleid nimetatakse mõnikord System One mudeliteks.",
"docsLink": "Kuidas see töötab",
"side": "Tulemused, näited ja kood",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Viimased 7 päeva",
"month": "Viimased 30 päeva",
"older": "Vanemad"
}
},
"length": "Pikkus",
"tags": "Stiiliideed",
"submitting": "Saadan su laulu stuudiosse…",
"needStyle": "Laulu loomiseks kirjelda stiili.",
"needLength": "Vali pikkus vahemikus {{min}}–{{max}} sekundit.",
"close": "Sulge",
"pause": "Paus",
"seek": "Asukoht laulus"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "گفتگو",
"images": "تصاویر",
"music": "موسیقی",
"playground": "زمین آزمایش",
"decisions": "تصمیم‌ها",
"mission": "مأموریت",
"login": "ورود",
"register": "ثبت‌نام",
@@ -1,7 +1,7 @@
{
"page": {
"title": "زمین آزمایش",
"lead": "از یک مدل تصمیم‌گیری دربارهٔ هر متن یا JSON پرسش‌های نوع‌دار بپرسید و در یک گذر پاسخ‌های کالیبره‌شده بگیرید — بدون متن تولیدشده.",
"title": "تصمیم‌ها",
"lead": "دربارهٔ هر متن یا JSON پرسش‌های بله/خیر، چندگزینه‌ای یا امتیازدهی بپرسید و فوراً پاسخ‌هایی با اطمینان کالیبره‌شده بگیرید. به چنین مدل‌هایی گاهی مدل‌های System One می‌گویند.",
"docsLink": "چطور کار می‌کند",
"side": "نتایج، نمونه‌ها و کد",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "۷ روز گذشته",
"month": "۳۰ روز گذشته",
"older": "قدیمی‌تر"
}
},
"length": "مدت",
"tags": "ایده‌های سبک",
"submitting": "در حال ارسال آهنگ شما به استودیو…",
"needStyle": "برای ساخت آهنگ، یک سبک توصیف کنید.",
"needLength": "مدتی بین {{min}} تا {{max}} ثانیه انتخاب کنید.",
"close": "بستن",
"pause": "مکث",
"seek": "موقعیت در آهنگ"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Keskustelu",
"images": "Kuvat",
"music": "Musiikki",
"playground": "Hiekkalaatikko",
"decisions": "Päätökset",
"mission": "Missio",
"login": "Kirjaudu sisään",
"register": "Rekisteröidy",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Hiekkalaatikko",
"lead": "Esitä päätösmallille tyypitettyjä kysymyksiä mistä tahansa tekstistä tai JSONista ja saat kalibroidut vastaukset yhdellä läpikäynnillä — ilman tuotettua tekstiä.",
"title": "Päätökset",
"lead": "Esitä kyllä/ei-, monivalinta- tai pisteytyskysymyksiä mistä tahansa tekstistä tai JSONista ja saat heti vastaukset kalibroidulla varmuudella. Tällaisia malleja kutsutaan joskus System One -malleiksi.",
"docsLink": "Miten se toimii",
"side": "Tulokset, esimerkit ja koodi",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Edelliset 7 päivää",
"month": "Edelliset 30 päivää",
"older": "Vanhemmat"
}
},
"length": "Pituus",
"tags": "Tyyli-ideoita",
"submitting": "Kappaletta lähetetään studioon…",
"needStyle": "Kuvaile tyyli luodaksesi kappaleen.",
"needLength": "Valitse pituus väliltä {{min}}–{{max}} sekuntia.",
"close": "Sulje",
"pause": "Tauko",
"seek": "Kohta kappaleessa"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Chat",
"images": "Images",
"music": "Musique",
"playground": "Bac à sable",
"decisions": "Décisions",
"mission": "Mission",
"login": "Connexion",
"register": "Inscription",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Bac à sable",
"lead": "Posez à un modèle de décision des questions typées sur n’importe quel texte ou JSON, et obtenez des réponses calibrées en une seule passe — sans texte généré.",
"title": "Décisions",
"lead": "Posez des questions oui/non, à choix multiples ou de notation sur n’importe quel texte ou JSON, et obtenez instantanément des réponses avec une confiance calibrée. On appelle parfois ces modèles des modèles System One.",
"docsLink": "Comment ça marche",
"side": "Résultats, exemples et code",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "7 derniers jours",
"month": "30 derniers jours",
"older": "Plus anciens"
}
},
"length": "Durée",
"tags": "Idées de style",
"submitting": "Envoi de votre chanson au studio…",
"needStyle": "Décrivez un style pour générer une chanson.",
"needLength": "Choisissez une durée entre {{min}} et {{max}} secondes.",
"close": "Fermer",
"pause": "Pause",
"seek": "Position dans la chanson"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Comhrá",
"images": "Íomhánna",
"music": "Ceol",
"playground": "Clós súgartha",
"decisions": "Cinntí",
"mission": "Misean",
"login": "Sínigh isteach",
"register": "Cláraigh",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Clós súgartha",
"lead": "Cuir ceisteanna clóscríofa ar mhúnla cinntí faoi théacs nó JSON ar bith agus faigh freagraí calabraithe in aon phas amháin — gan téacs ginte.",
"title": "Cinntí",
"lead": "Cuir ceisteanna tá/níl, ilrogha nó scórála faoi théacs nó JSON ar bith agus faigh freagraí láithreacha le muinín calabraithe. Uaireanta tugtar samhlacha System One ar shamhlacha mar seo.",
"docsLink": "Conas a oibríonn sé",
"side": "Torthaí, samplaí agus cód",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Na 7 lá seo caite",
"month": "Na 30 lá seo caite",
"older": "Níos sine"
}
},
"length": "Fad",
"tags": "Smaointe stíle",
"submitting": "Do d’amhrán á sheoladh chuig an stiúideo…",
"needStyle": "Déan cur síos ar stíl chun amhrán a ghiniúint.",
"needLength": "Roghnaigh fad idir {{min}} agus {{max}} soicind.",
"close": "Dún",
"pause": "Sos",
"seek": "Suíomh san amhrán"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Tattaunawa",
"images": "Hotuna",
"music": "Kiɗa",
"playground": "Filin gwaji",
"decisions": "Shawarwari",
"mission": "Manufa",
"login": "Shiga",
"register": "Yi rajista",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Filin gwaji",
"lead": "Yi wa samfurin yanke shawara tambayoyi masu nau’i game da kowane rubutu ko JSON, ka sami amsoshi da aka daidaita a zagaye ɗaya — ba tare da rubutun da aka ƙirƙira ba.",
"title": "Shawarwari",
"lead": "Yi tambayoyin e/a'a, na zaɓi daga zaɓuɓɓuka ko na maki game da kowane rubutu ko JSON, ka sami amsoshi nan take tare da tabbaci da aka daidaita. A wasu lokuta ana kiran irin waɗannan samfura samfuran System One.",
"docsLink": "Yadda yake aiki",
"side": "Sakamako, misalai da lamba",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Kwanaki 7 da suka gabata",
"month": "Kwanaki 30 da suka gabata",
"older": "Tsofaffi"
}
},
"length": "Tsawo",
"tags": "Ra'ayoyin salo",
"submitting": "Ana aika wakarka zuwa sitiriyo…",
"needStyle": "Bayyana salo don ƙirƙirar waƙa.",
"needLength": "Zaɓi tsawo tsakanin daƙiƙa {{min}} da {{max}}.",
"close": "Rufe",
"pause": "Dakata",
"seek": "Matsayi a cikin waƙa"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "צ'אט",
"images": "תמונות",
"music": "מוזיקה",
"playground": "ארגז חול",
"decisions": "החלטות",
"mission": "חזון",
"login": "התחברות",
"register": "הרשמה",
@@ -1,7 +1,7 @@
{
"page": {
"title": "ארגז חול",
"lead": "שאלו מודל החלטות שאלות מוקלדות על כל טקסט או JSON וקבלו תשובות מכוילות במעבר אחד — בלי טקסט מחולל.",
"title": "החלטות",
"lead": "שאלו שאלות כן/לא, רב־ברירה או דירוג על כל טקסט או JSON וקבלו מיד תשובות עם ודאות מכוילת. מודלים כאלה נקראים לפעמים מודלי System One.",
"docsLink": "איך זה עובד",
"side": "תוצאות, דוגמאות וקוד",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "7 הימים האחרונים",
"month": "30 הימים האחרונים",
"older": "ישנים יותר"
}
},
"length": "אורך",
"tags": "רעיונות לסגנון",
"submitting": "השיר שלך נשלח לאולפן…",
"needStyle": "תארו סגנון כדי ליצור שיר.",
"needLength": "בחרו אורך בין {{min}} ל־{{max}} שניות.",
"close": "סגירה",
"pause": "השהיה",
"seek": "מיקום בשיר"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Razgovor",
"images": "Slike",
"music": "Glazba",
"playground": "Igralište",
"decisions": "Odluke",
"mission": "Misija",
"login": "Prijava",
"register": "Registracija",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Igralište",
"lead": "Postavite modelu za odlučivanje tipizirana pitanja o bilo kojem tekstu ili JSON-u i dobijte kalibrirane odgovore u jednom prolazu — bez generiranog teksta.",
"title": "Odluke",
"lead": "Postavljajte pitanja da/ne, s višestrukim izborom ili s ocjenjivanjem o bilo kojem tekstu ili JSON-u i odmah dobijte odgovore s kalibriranom pouzdanošću. Takvi se modeli ponekad nazivaju modelima System One.",
"docsLink": "Kako radi",
"side": "Rezultati, primjeri i kod",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Zadnjih 7 dana",
"month": "Zadnjih 30 dana",
"older": "Starije"
}
},
"length": "Trajanje",
"tags": "Ideje za stil",
"submitting": "Šaljem vašu pjesmu u studio…",
"needStyle": "Opišite stil kako biste generirali pjesmu.",
"needLength": "Odaberite trajanje između {{min}} i {{max}} sekundi.",
"close": "Zatvori",
"pause": "Pauza",
"seek": "Položaj u pjesmi"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Csevegés",
"images": "Képek",
"music": "Zene",
"playground": "Játszótér",
"decisions": "Döntések",
"mission": "Küldetés",
"login": "Bejelentkezés",
"register": "Regisztráció",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Játszótér",
"lead": "Tegyen fel egy döntési modellnek típusos kérdéseket bármilyen szövegről vagy JSON-ról, és kapjon kalibrált válaszokat egyetlen lépésben — generált szöveg nélkül.",
"title": "Döntések",
"lead": "Tegyen fel igen/nem, feleletválasztós vagy pontozó kérdéseket bármilyen szövegről vagy JSON-ról, és azonnal kalibrált megbízhatóságú válaszokat kap. Az ilyen modelleket néha System One modelleknek nevezik.",
"docsLink": "Hogyan működik",
"side": "Eredmények, példák és kód",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Elmúlt 7 nap",
"month": "Elmúlt 30 nap",
"older": "Régebbi"
}
},
"length": "Hossz",
"tags": "Stílusötletek",
"submitting": "A dal küldése a stúdióba…",
"needStyle": "Írjon le egy stílust a dal létrehozásához.",
"needLength": "Válasszon {{min}} és {{max}} másodperc közötti hosszt.",
"close": "Bezárás",
"pause": "Szünet",
"seek": "Pozíció a dalban"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Chat",
"images": "Immagini",
"music": "Musica",
"playground": "Playground",
"decisions": "Decisioni",
"mission": "Missione",
"login": "Accedi",
"register": "Registrati",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Playground",
"lead": "Poni a un modello decisionale domande tipizzate su qualsiasi testo o JSON e ottieni risposte calibrate in un solo passaggio, senza testo generato.",
"title": "Decisioni",
"lead": "Fai domande sì/no, a scelta multipla o di punteggio su qualsiasi testo o JSON e ottieni subito risposte con una confidenza calibrata. Modelli di questo tipo sono talvolta chiamati modelli System One.",
"docsLink": "Come funziona",
"side": "Risultati, esempi e codice",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Ultimi 7 giorni",
"month": "Ultimi 30 giorni",
"older": "Meno recenti"
}
},
"length": "Durata",
"tags": "Idee di stile",
"submitting": "Invio della canzone allo studio…",
"needStyle": "Descrivi uno stile per generare una canzone.",
"needLength": "Scegli una durata tra {{min}} e {{max}} secondi.",
"close": "Chiudi",
"pause": "Pausa",
"seek": "Posizione nella canzone"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "ჩატი",
"images": "სურათები",
"music": "მუსიკა",
"playground": "სათამაშო მოედანი",
"decisions": "გადაწყვეტილებები",
"mission": "მისია",
"login": "შესვლა",
"register": "რეგისტრაცია",
@@ -1,7 +1,7 @@
{
"page": {
"title": "სათამაშო მოედანი",
"lead": "დაუსვით გადაწყვეტილების მოდელს ტიპიზებული კითხვები ნებისმიერ ტექსტსა თუ JSON-ზე და მიიღეთ კალიბრირებული პასუხები ერთ გავლაში — გენერირებული ტექსტის გარეშე.",
"title": "გადაწყვეტილებები",
"lead": "დასვით კი/არა, არჩევითი ან შეფასების კითხვები ნებისმიერ ტექსტზე ან JSON-ზე და მყისიერად მიიღეთ პასუხები დაკალიბრებული სანდოობით. ასეთ მოდელებს ზოგჯერ System One მოდელებს უწოდებენ.",
"docsLink": "როგორ მუშაობს",
"side": "შედეგები, მაგალითები და კოდი",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "ბოლო 7 დღე",
"month": "ბოლო 30 დღე",
"older": "უფრო ძველი"
}
},
"length": "ხანგრძლივობა",
"tags": "სტილის იდეები",
"submitting": "თქვენი სიმღერა სტუდიაში იგზავნება…",
"needStyle": "სიმღერის შესაქმნელად აღწერეთ სტილი.",
"needLength": "აირჩიეთ ხანგრძლივობა {{min}}-დან {{max}} წამამდე.",
"close": "დახურვა",
"pause": "პაუზა",
"seek": "პოზიცია სიმღერაში"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Чат",
"images": "Кескіндер",
"music": "Музыка",
"playground": "Сынақ алаңы",
"decisions": "Шешімдер",
"mission": "Миссия",
"login": "Кіру",
"register": "Тіркелу",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Сынақ алаңы",
"lead": "Шешім моделіне кез келген мәтін не JSON туралы типтелген сұрақтар қойып, бір өтуде калибрленген жауаптар алыңыз — мәтін генерацияланбайды.",
"title": "Шешімдер",
"lead": "Кез келген мәтін немесе JSON туралы иә/жоқ, таңдау немесе бағалау сұрақтарын қойып, калибрленген сенімділікпен лезде жауап алыңыз. Мұндай модельдерді кейде System One модельдері деп атайды.",
"docsLink": "Қалай жұмыс істейді",
"side": "Нәтижелер, мысалдар және код",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Соңғы 7 күн",
"month": "Соңғы 30 күн",
"older": "Ескірек"
}
},
"length": "Ұзақтығы",
"tags": "Стиль идеялары",
"submitting": "Әніңіз студияға жіберілуде…",
"needStyle": "Ән жасау үшін стильді сипаттаңыз.",
"needLength": "{{min}} мен {{max}} секунд аралығындағы ұзақтықты таңдаңыз.",
"close": "Жабу",
"pause": "Кідірту",
"seek": "Әндегі орын"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Pokalbis",
"images": "Vaizdai",
"music": "Muzika",
"playground": "Smėlio dėžė",
"decisions": "Sprendimai",
"mission": "Misija",
"login": "Prisijungti",
"register": "Registruotis",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Smėlio dėžė",
"lead": "Užduokite sprendimų modeliui tipizuotus klausimus apie bet kokį tekstą ar JSON ir gaukite kalibruotus atsakymus vienu perėjimu — be sugeneruoto teksto.",
"title": "Sprendimai",
"lead": "Užduokite taip/ne, pasirinkimo ar vertinimo klausimus apie bet kokį tekstą ar JSON ir iškart gaukite atsakymus su kalibruotu patikimumu. Tokie modeliai kartais vadinami System One modeliais.",
"docsLink": "Kaip tai veikia",
"side": "Rezultatai, pavyzdžiai ir kodas",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Paskutinės 7 dienos",
"month": "Paskutinės 30 dienų",
"older": "Senesni"
}
},
"length": "Trukmė",
"tags": "Stiliaus idėjos",
"submitting": "Jūsų daina siunčiama į studiją…",
"needStyle": "Aprašykite stilių, kad sukurtumėte dainą.",
"needLength": "Pasirinkite trukmę nuo {{min}} iki {{max}} sekundžių.",
"close": "Uždaryti",
"pause": "Pristabdyti",
"seek": "Vieta dainoje"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Tērzēšana",
"images": "Attēli",
"music": "Mūzika",
"playground": "Smilškaste",
"decisions": "Lēmumi",
"mission": "Misija",
"login": "Pieteikties",
"register": "Reģistrēties",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Smilškaste",
"lead": "Uzdodiet lēmumu modelim tipizētus jautājumus par jebkuru tekstu vai JSON un saņemiet kalibrētas atbildes vienā piegājienā — bez ģenerēta teksta.",
"title": "Lēmumi",
"lead": "Uzdodiet jā/nē, atbilžu izvēles vai vērtēšanas jautājumus par jebkuru tekstu vai JSON un nekavējoties saņemiet atbildes ar kalibrētu ticamību. Šādus modeļus dažkārt sauc par System One modeļiem.",
"docsLink": "Kā tas darbojas",
"side": "Rezultāti, piemēri un kods",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Pēdējās 7 dienas",
"month": "Pēdējās 30 dienas",
"older": "Vecāki"
}
},
"length": "Ilgums",
"tags": "Stila idejas",
"submitting": "Sūta jūsu dziesmu uz studiju…",
"needStyle": "Aprakstiet stilu, lai ģenerētu dziesmu.",
"needLength": "Izvēlieties ilgumu no {{min}} līdz {{max}} sekundēm.",
"close": "Aizvērt",
"pause": "Pauze",
"seek": "Pozīcija dziesmā"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "المحادثة",
"images": "Tiwlafin",
"music": "الموسيقى",
"playground": "بلاصة التجريب",
"decisions": "القرارات",
"mission": "المهمة",
"login": "دخول",
"register": "تسجيل",
@@ -1,7 +1,7 @@
{
"page": {
"title": "بلاصة التجريب",
"lead": "سوّل نموذج ديال القرارات أسئلة محددة النوع على أي نص ولا JSON، وخود أجوبة معايرة فدوزة وحدة — بلا نص مولّد.",
"title": "القرارات",
"lead": "سوّل أسئلة ب إيه/لا، ولا بالاختيار، ولا بالتنقيط على أي نص ولا JSON، وخود أجوبة فالحين بثقة معايرة. هاد النماذج كيتسماو شي مرات نماذج System One.",
"docsLink": "كيفاش خدام",
"side": "النتائج، الأمثلة والكود",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "آخر 7 يام",
"month": "آخر 30 يوم",
"older": "القدام"
}
},
"length": "الطول",
"tags": "أفكار ديال الستيل",
"submitting": "كنصيفطو الأغنية ديالك للستوديو…",
"needStyle": "وصف شي ستيل باش تصايب أغنية.",
"needLength": "ختار طول بين {{min}} و{{max}} ثانية.",
"close": "سد",
"pause": "وقّف",
"seek": "البلاصة فالأغنية"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Chat",
"images": "Immaġni",
"music": "Mużika",
"playground": "Spazju tal-provi",
"decisions": "Deċiżjonijiet",
"mission": "Missjoni",
"login": "Idħol",
"register": "Irreġistra",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Spazju tal-provi",
"lead": "Staqsi lil mudell tad-deċiżjonijiet mistoqsijiet tipizzati dwar kwalunkwe test jew JSON u ikseb tweġibiet kalibrati f’passaġġ wieħed — mingħajr test iġġenerat.",
"title": "Deċiżjonijiet",
"lead": "Staqsi mistoqsijiet iva/le, b’għażla multipla jew ta’ punteġġ dwar kwalunkwe test jew JSON, u ikseb tweġibiet immedjati b’kunfidenza kkalibrata. Mudelli bħal dawn xi drabi jissejħu mudelli System One.",
"docsLink": "Kif jaħdem",
"side": "Riżultati, eżempji u kodiċi",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "L-aħħar 7 ijiem",
"month": "L-aħħar 30 jum",
"older": "Eqdem"
}
},
"length": "Tul",
"tags": "Ideat tal-istil",
"submitting": "Qed nibagħtu l-kanzunetta tiegħek lill-istudjo…",
"needStyle": "Iddeskrivi stil biex tiġġenera kanzunetta.",
"needLength": "Agħżel tul bejn {{min}} u {{max}} sekonda.",
"close": "Agħlaq",
"pause": "Pawża",
"seek": "Pożizzjoni fil-kanzunetta"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Chat",
"images": "Afbeeldingen",
"music": "Muziek",
"playground": "Playground",
"decisions": "Beslissingen",
"mission": "Missie",
"login": "Inloggen",
"register": "Registreren",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Playground",
"lead": "Stel een beslismodel getypeerde vragen over elke tekst of JSON en krijg gekalibreerde antwoorden in één keer — zonder gegenereerde tekst.",
"title": "Beslissingen",
"lead": "Stel ja/nee-, meerkeuze- of scorevragen over willekeurige tekst of JSON en krijg direct antwoorden met gekalibreerde zekerheid. Zulke modellen worden soms System One-modellen genoemd.",
"docsLink": "Hoe het werkt",
"side": "Resultaten, voorbeelden en code",
"tabs": {
+9 -1
View File
@@ -41,5 +41,13 @@
"week": "Afgelopen 7 dagen",
"month": "Afgelopen 30 dagen",
"older": "Ouder"
}
},
"length": "Lengte",
"tags": "Stijlideeën",
"submitting": "Uw nummer wordt naar de studio gestuurd…",
"needStyle": "Beschrijf een stijl om een nummer te genereren.",
"needLength": "Kies een lengte tussen {{min}} en {{max}} seconden.",
"close": "Sluiten",
"pause": "Pauzeren",
"seek": "Positie in het nummer"
}
+1 -1
View File
@@ -8,7 +8,7 @@
"chat": "Chat",
"images": "Bilder",
"music": "Musikk",
"playground": "Lekeplass",
"decisions": "Beslutninger",
"mission": "Misjon",
"login": "Logg inn",
"register": "Registrer deg",
@@ -1,7 +1,7 @@
{
"page": {
"title": "Lekeplass",
"lead": "Still en beslutningsmodell typede spørsmål om hvilken som helst tekst eller JSON, og få kalibrerte svar i én enkelt runde — uten generert tekst.",
"title": "Beslutninger",
"lead": "Still ja/nei-, flervalgs- eller poengspørsmål om hvilken som helst tekst eller JSON, og få umiddelbare svar med kalibrert sikkerhet. Slike modeller kalles noen ganger System One-modeller.",
"docsLink": "Slik fungerer det",
"side": "Resultater, eksempler og kode",
"tabs": {

Some files were not shown because too many files have changed in this diff Show More