feat(app): the operator's overview of screens, grants to several people at once with one budget each, assistants by provider
Halkyon Learn's ADR 0021, D57 to D63, on halkyon_base's budgets and effort: - The operator's screen is an overview (Waiting, Operators, Accounts, Assistants, each with its count) whose rows open screens of their own, read 50 at a time as they scroll (OperatorListCubit, OperatorPage), so hundreds of accounts push nothing else down (D62). - A handle is picked among the accounts there are (AccountSuggestionField): appointing an operator and granting take no other (D61). - AssistantGrantsScreen replaces AssistantGrantsCard: the assistants set up by provider, with their efforts and prices; the people granted, found by their handle's start; one sheet granting several assistants, a provider's at once, to several people, each with one budget in money and tokens a day and a month, or changing one person's (D58, D59). - Assistants are grouped by provider wherever they are listed, each under its name with a dot in its colour (ProviderLabel, D60). - AssistantsCard shows what is left of the person's budget for all their assistants together; the months of the figures run oldest first (D57). - AssistantChoice says the efforts a question may ask at and the ceiling on output each asks for (D63); a person's own key gets none and keeps 4,096 tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
24ed63585e
commit
cc87120bc8
52 changed files with 5348 additions and 2709 deletions
|
|
@ -9,8 +9,8 @@ Halkyon's apps and the packages they share, in one repository and one pub worksp
|
||||||
- **`apps/<app>/`**: each app's packages, as `serverpod create` lays them out (`halkyon_<app>_server`, `_client`, `_flutter`), plus what is the app's alone (Learn's curator, the scripts writing Learn's assets in `apps/learn/tool/`, run from `apps/learn`). Learn is the app that runs; Todo is still Serverpod's scaffold; Train and Meditate are to come.
|
- **`apps/<app>/`**: each app's packages, as `serverpod create` lays them out (`halkyon_<app>_server`, `_client`, `_flutter`), plus what is the app's alone (Learn's curator, the scripts writing Learn's assets in `apps/learn/tool/`, run from `apps/learn`). Learn is the app that runs; Todo is still Serverpod's scaffold; Train and Meditate are to come.
|
||||||
- **`packages/`**: what the apps share, each with its README and licence, which the apps take by path (`path: ../../../packages/<package>`), so a change to a shared package and to the apps using it is one commit:
|
- **`packages/`**: what the apps share, each with its README and licence, which the apps take by path (`path: ../../../packages/<package>`), so a change to a shared package and to the apps using it is one commit:
|
||||||
- `halkyon_ui`: Halkyon's look, the theme around cobalt and copper, Noto Sans bundled, the window-size classes and the widgets every screen is built of, with texts of its own (English, German).
|
- `halkyon_ui`: Halkyon's look, the theme around cobalt and copper, Noto Sans bundled, the window-size classes and the widgets every screen is built of, with texts of its own (English, German).
|
||||||
- `halkyon_base_server`, `halkyon_base_client`: the Serverpod module every Halkyon server includes (ADR 0014): signing in (a password with TOTP and the resets of ADR 0017, SimpleLogin on `halkyon_oidc`, email codes proving an address; no anonymous accounts since ADR 0020, D25, nor GitHub since D36), an account's ways in and its deletion with a hook for each app's own data, the pruning, the instance's basics, people's handles and public profiles, who may sign up (`SIGN_UP`: open, approval, closed) with the accounts waiting for approval and the instance's operators who approve them (Halkyon Learn's ADR 0021, step 1), the operator's assistants forwarded on halkyon_llm with their grants, limits and counted figures, and the instance's privacy page made from its settings (step 2), and the instance's register (ADR 0016): one account per person across an operator's services, held by one service (Learn's server on learn.halkyon.eu) and asked by the others, its members (`ACCOUNT_REGISTER_URL`), all sharing a secret (`INSTANCE_SECRET`); its README says how.
|
- `halkyon_base_server`, `halkyon_base_client`: the Serverpod module every Halkyon server includes (ADR 0014): signing in (a password with TOTP and the resets of ADR 0017, SimpleLogin on `halkyon_oidc`, email codes proving an address; no anonymous accounts since ADR 0020, D25, nor GitHub since D36), an account's ways in and its deletion with a hook for each app's own data, the pruning, the instance's basics, people's handles and public profiles, who may sign up (`SIGN_UP`: open, approval, closed) with the accounts waiting for approval and the instance's operators who approve them (Halkyon Learn's ADR 0021, step 1), the operator's assistants forwarded on halkyon_llm with their grants, each person's budget, the efforts they allow and counted figures, and the instance's privacy page made from its settings (step 2), and the instance's register (ADR 0016): one account per person across an operator's services, held by one service (Learn's server on learn.halkyon.eu) and asked by the others, its members (`ACCOUNT_REGISTER_URL`), all sharing a secret (`INSTANCE_SECRET`); its README says how.
|
||||||
- `halkyon_app`: every app's shell on the module's client (ADR 0014): the instances a device keeps accounts on and the first start, signing in and switching person, the gates building an app's dependencies per instance and per person, the sync service and where it stands, the account section of an app's settings (`AccountTiles`), the wait screen of an account waiting for approval and the operator's card, badge and screen (ADR 0021), the assistants of an app's settings (`AssistantsCard`: granted, agreed to, used, and the person's own keys) and the operator's grants on that screen (`AssistantGrantsCard`), with the repositories asking them through the module or a person's own key directly (step 2) and what an app's warning before a private studyset goes names (`providerTermsOf`, `OwnAssistant.onThisMachine`, step 3), About with the instance's source link (`showAppAbout`), the language every app picks (`resolveAppLocale`) and countries' and subdivisions' names (Unicode CLDR's, bundled), with texts that name the app.
|
- `halkyon_app`: every app's shell on the module's client (ADR 0014): the instances a device keeps accounts on and the first start, signing in and switching person, the gates building an app's dependencies per instance and per person, the sync service and where it stands, the account section of an app's settings (`AccountTiles`), the wait screen of an account waiting for approval and the operator's card, badge and screen (ADR 0021), the assistants of an app's settings (`AssistantsCard`: granted, by provider, agreed to, what is left of the person's budget, used, and the person's own keys) and the operator's grants from that screen (`AssistantGrantsScreen`: several assistants to several people at once, each with one budget, D57 to D63), with the repositories asking them through the module, at an effort the operator allows, or a person's own key directly (step 2) and what an app's warning before a private studyset goes names (`providerTermsOf`, `OwnAssistant.onThisMachine`, step 3), About with the instance's source link (`showAppAbout`), the language every app picks (`resolveAppLocale`) and countries' and subdivisions' names (Unicode CLDR's, bundled), with texts that name the app.
|
||||||
- `halkyon_core` (low-level primitives in pure Dart: ids, a logger, `SerialQueue`, `CoalescingRunner`) and `halkyon_interop` (contracts between apps, pure Dart).
|
- `halkyon_core` (low-level primitives in pure Dart: ids, a logger, `SerialQueue`, `CoalescingRunner`) and `halkyon_interop` (contracts between apps, pure Dart).
|
||||||
- `halkyon_keys` (pure Dart on libsodium, ADR 0017): an account's keys on the device, the password's Argon2id and SRP's x, the key pairs and their wrapping, the recovery phrase, data keys, content sealed and signed. SRP-6a's two halves are the module's (`halkyon_base_server`'s `base.dart`, `halkyon_base_client`'s `srp.dart`), as are TOTP and the recovery codes. `sodium`'s build hook compiles libsodium, so every machine that builds or tests needs a C compiler and `make`.
|
- `halkyon_keys` (pure Dart on libsodium, ADR 0017): an account's keys on the device, the password's Argon2id and SRP's x, the key pairs and their wrapping, the recovery phrase, data keys, content sealed and signed. SRP-6a's two halves are the module's (`halkyon_base_server`'s `base.dart`, `halkyon_base_client`'s `srp.dart`), as are TOTP and the recovery codes. `sodium`'s build hook compiles libsodium, so every machine that builds or tests needs a C compiler and `make`.
|
||||||
- `halkyon_llm` (pure Dart, Halkyon Learn's ADR 0021, D15 and D46): language models' APIs streamed over plain HTTP, no SDKs: Anthropic's Messages API, OpenAI's chat completions (each known host's fields, D45) and its Responses API, behind one interface (`LlmClient`), with `FakeLlmClient` for tests; tested on replies written from the providers' documentation and on those `tool/record_llm.dart` records with a key (D36). The module forwards the operator's assistants with it, and the apps call a person's own provider. BSD-3-Clause as `halkyon_oidc`, `publish_to: none`.
|
- `halkyon_llm` (pure Dart, Halkyon Learn's ADR 0021, D15 and D46): language models' APIs streamed over plain HTTP, no SDKs: Anthropic's Messages API, OpenAI's chat completions (each known host's fields, D45) and its Responses API, behind one interface (`LlmClient`), with `FakeLlmClient` for tests; tested on replies written from the providers' documentation and on those `tool/record_llm.dart` records with a key (D36). The module forwards the operator's assistants with it, and the apps call a person's own provider. BSD-3-Clause as `halkyon_oidc`, `publish_to: none`.
|
||||||
|
|
|
||||||
|
|
@ -467,6 +467,9 @@ void main() {
|
||||||
|
|
||||||
await tester.tap(find.text('Accounts and operators'));
|
await tester.tap(find.text('Accounts and operators'));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
// The operator's overview (D62), whose Waiting lists them.
|
||||||
|
await tester.tap(find.text('Waiting'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
expect(find.text('@ben'), findsOneWidget);
|
expect(find.text('@ben'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,26 +62,40 @@ Written as the apps are: Dart 3.13's primary constructors, Material from
|
||||||
the person's app opens, after each change, and every 5 minutes only while
|
the person's app opens, after each change, and every 5 minutes only while
|
||||||
they operate it), `InstanceOperatorCard` or `InstanceOperatorTiles` for an
|
they operate it), `InstanceOperatorCard` or `InstanceOperatorTiles` for an
|
||||||
app's settings and `WaitingAccountsBadge` for its Settings cog, both
|
app's settings and `WaitingAccountsBadge` for its Settings cog, both
|
||||||
nothing at all for anyone else, and `InstanceOperatorScreen`: the accounts
|
nothing at all for anyone else, and `InstanceOperatorScreen` (D62): an
|
||||||
that wait, approved once their person confirmed the words read out, or
|
overview of how many wait, operate and have accounts, and of the
|
||||||
refused; who operates, named by handle and taken back (never the last);
|
assistants set up, each row opening a screen of its own, every list read
|
||||||
accounts found by the start of their handle, suspended or named. Only the
|
50 at a time as it is scrolled (`OperatorListCubit`, `OperatorPage`): the
|
||||||
register's service answers: on a member, nothing shows.
|
accounts that wait, approved once their person confirmed the words read
|
||||||
|
out, or refused; who operates, named by a handle picked among those
|
||||||
|
suggested and taken back (never the last); accounts found by the start of
|
||||||
|
their handle, suspended, named or granted assistants. A handle is only
|
||||||
|
ever picked among the accounts there are (`AccountSuggestionField`, D61).
|
||||||
|
Only the register's service answers: on a member, nothing shows.
|
||||||
- **Assistants** (`lib/src/assistant/`, Halkyon Learn's ADR 0021, step 2):
|
- **Assistants** (`lib/src/assistant/`, Halkyon Learn's ADR 0021, step 2):
|
||||||
`AssistantRepository` on `ServerpodAssistantGateway` (the module's
|
`AssistantRepository` on `ServerpodAssistantGateway` (the module's
|
||||||
`assistant` endpoint; refusals as `AssistantFailed`) asks an
|
`assistant` endpoint; refusals as `AssistantFailed`) asks an
|
||||||
`AssistantChoice` in halkyon_llm's terms: a `GrantedAssistant` through the
|
`AssistantChoice` in halkyon_llm's terms: a `GrantedAssistant` through the
|
||||||
instance, which counts it, or a `PersonalAssistant` with the person's own
|
instance, which counts it, at an effort among those the operator allows
|
||||||
key directly (`LlmClient`), the instance seeing neither key nor question;
|
and with that effort's ceiling on output (`efforts`, `outputCeilingAt`,
|
||||||
|
D63), or a `PersonalAssistant` with the person's own key directly
|
||||||
|
(`LlmClient`), without an effort and at 4,096 output tokens, the instance
|
||||||
|
seeing neither key nor question;
|
||||||
`assistant_messages.dart` turns a conversation into the endpoint's models
|
`assistant_messages.dart` turns a conversation into the endpoint's models
|
||||||
and back, part by part. `AssistantsCard` is an app's settings' card: what
|
and back, part by part. `AssistantsCard` is an app's settings' card: what
|
||||||
is granted and left of its limits, agreed to once (the consent sheet),
|
is granted, under each provider's name in its colour (`ProviderLabel`,
|
||||||
the person's use by UTC month, the instance's privacy page, and the
|
`providerColorIn`, D60), what is left of the person's budget for them
|
||||||
|
together (D58), each agreed to once (the consent sheet), the person's use
|
||||||
|
by UTC month, oldest first (D57), the instance's privacy page, and the
|
||||||
person's own assistants where the operator allows them, kept where the
|
person's own assistants where the operator allows them, kept where the
|
||||||
app says (`OwnAssistantStore`: Learn's sealed settings). For the operator,
|
app says (`OwnAssistantStore`: Learn's sealed settings). For the operator,
|
||||||
`AssistantOperatorRepository` (grants by exact handle) and
|
`AssistantOperatorRepository` and `AssistantGrantsScreen`, opened from
|
||||||
`AssistantGrantsCard` on `InstanceOperatorScreen`: grants with their
|
`InstanceOperatorScreen`: the assistants set up, by provider, with their
|
||||||
limits, granted, changed and taken back, and everyone's use by month.
|
efforts and whether they are priced; the people granted some, each with
|
||||||
|
one budget in money and tokens, a day and a month (D58, D59), found by
|
||||||
|
their handle's start; one sheet granting several assistants, a
|
||||||
|
provider's at once, to several people at once, or changing one person's
|
||||||
|
(`showAssistantGrantSheet`); and everyone's use by month.
|
||||||
`package:halkyon_app/assistant_models.dart` holds the models alone, in
|
`package:halkyon_app/assistant_models.dart` holds the models alone, in
|
||||||
pure Dart, for an app's own models. For the app's warnings before a
|
pure Dart, for an app's own models. For the app's warnings before a
|
||||||
private studyset's content goes (ADR 0021, D9): `providerTermsOf` names a
|
private studyset's content goes (ADR 0021, D9): `providerTermsOf` names a
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,6 @@ export 'src/assistant/model/assistant_amount.dart';
|
||||||
export 'src/assistant/model/assistant_failed.dart';
|
export 'src/assistant/model/assistant_failed.dart';
|
||||||
export 'src/assistant/model/assistant_grant.dart';
|
export 'src/assistant/model/assistant_grant.dart';
|
||||||
export 'src/assistant/model/assistant_offer.dart';
|
export 'src/assistant/model/assistant_offer.dart';
|
||||||
|
export 'src/assistant/model/assistant_provider.dart';
|
||||||
export 'src/assistant/model/own_assistant.dart';
|
export 'src/assistant/model/own_assistant.dart';
|
||||||
export 'src/assistant/model/provider_terms.dart';
|
export 'src/assistant/model/provider_terms.dart';
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,14 @@
|
||||||
/// - **The instance's operators**: `InstanceOperatorCard` (or
|
/// - **The instance's operators**: `InstanceOperatorCard` (or
|
||||||
/// `InstanceOperatorTiles`) and `WaitingAccountsBadge` for whoever
|
/// `InstanceOperatorTiles`) and `WaitingAccountsBadge` for whoever
|
||||||
/// operates the instance, on `OperatingCubit`, leading to
|
/// operates the instance, on `OperatingCubit`, leading to
|
||||||
/// `InstanceOperatorScreen`, where accounts are approved, refused and
|
/// `InstanceOperatorScreen`, an overview whose screens approve, refuse and
|
||||||
/// suspended and operators named (Halkyon Learn's ADR 0021, D21 to D24).
|
/// suspend accounts and name operators (Halkyon Learn's ADR 0021, D21 to
|
||||||
|
/// D24, D61, D62).
|
||||||
/// - **Assistants**: `AssistantsCard` for an app's settings, what the
|
/// - **Assistants**: `AssistantsCard` for an app's settings, what the
|
||||||
/// operator grants a person and their own keys where allowed, on
|
/// operator grants a person and their own keys where allowed, on
|
||||||
/// `AssistantRepository`; `AssistantGrantsCard` on the operator's screen,
|
/// `AssistantRepository`; `AssistantGrantsScreen` from the operator's
|
||||||
/// on `AssistantOperatorRepository` (Halkyon Learn's ADR 0021, step 2).
|
/// screen, on `AssistantOperatorRepository` (Halkyon Learn's ADR 0021,
|
||||||
|
/// step 2, D57 to D63).
|
||||||
/// `package:halkyon_app/assistant_models.dart` holds the models alone.
|
/// `package:halkyon_app/assistant_models.dart` holds the models alone.
|
||||||
/// - **Signing in**: with SimpleLogin (ADR 0020: `SimpleLoginRepository`,
|
/// - **Signing in**: with SimpleLogin (ADR 0020: `SimpleLoginRepository`,
|
||||||
/// its sheet and `SimpleLoginSection`), with a password and TOTP (ADR
|
/// its sheet and `SimpleLoginSection`), with a password and TOTP (ADR
|
||||||
|
|
@ -44,7 +46,7 @@ export 'src/app/app_locale.dart';
|
||||||
export 'src/app/app_scope.dart';
|
export 'src/app/app_scope.dart';
|
||||||
export 'src/app/instance_gate.dart';
|
export 'src/app/instance_gate.dart';
|
||||||
export 'src/app/person_scope.dart';
|
export 'src/app/person_scope.dart';
|
||||||
export 'src/assistant/bloc/assistant_grants_cubit.dart';
|
export 'src/assistant/bloc/assistant_grant_cubit.dart';
|
||||||
export 'src/assistant/bloc/assistant_use_cubit.dart';
|
export 'src/assistant/bloc/assistant_use_cubit.dart';
|
||||||
export 'src/assistant/bloc/assistants_cubit.dart';
|
export 'src/assistant/bloc/assistants_cubit.dart';
|
||||||
export 'src/assistant/data/assistant_gateway.dart';
|
export 'src/assistant/data/assistant_gateway.dart';
|
||||||
|
|
@ -62,9 +64,11 @@ export 'src/assistant/model/assistant_amount.dart';
|
||||||
export 'src/assistant/model/assistant_failed.dart';
|
export 'src/assistant/model/assistant_failed.dart';
|
||||||
export 'src/assistant/model/assistant_grant.dart';
|
export 'src/assistant/model/assistant_grant.dart';
|
||||||
export 'src/assistant/model/assistant_offer.dart';
|
export 'src/assistant/model/assistant_offer.dart';
|
||||||
|
export 'src/assistant/model/assistant_provider.dart';
|
||||||
export 'src/assistant/model/own_assistant.dart';
|
export 'src/assistant/model/own_assistant.dart';
|
||||||
export 'src/assistant/model/provider_terms.dart';
|
export 'src/assistant/model/provider_terms.dart';
|
||||||
export 'src/assistant/widget/assistant_grants_card.dart';
|
export 'src/assistant/widget/assistant_grants_screen.dart';
|
||||||
|
export 'src/assistant/widget/assistant_provider_view.dart';
|
||||||
export 'src/assistant/widget/assistant_use_view.dart';
|
export 'src/assistant/widget/assistant_use_view.dart';
|
||||||
export 'src/assistant/widget/assistant_words.dart';
|
export 'src/assistant/widget/assistant_words.dart';
|
||||||
export 'src/assistant/widget/assistants_card.dart';
|
export 'src/assistant/widget/assistants_card.dart';
|
||||||
|
|
@ -108,8 +112,10 @@ export 'src/instance/widget/instance_location.dart';
|
||||||
export 'src/instance/widget/instance_location_words.dart';
|
export 'src/instance/widget/instance_location_words.dart';
|
||||||
export 'src/instance/widget/instance_step.dart';
|
export 'src/instance/widget/instance_step.dart';
|
||||||
export 'src/instance/widget/instance_tile.dart';
|
export 'src/instance/widget/instance_tile.dart';
|
||||||
export 'src/operator/bloc/instance_operator_cubit.dart';
|
export 'src/operator/bloc/account_suggestions_cubit.dart';
|
||||||
export 'src/operator/bloc/operating_cubit.dart';
|
export 'src/operator/bloc/operating_cubit.dart';
|
||||||
|
export 'src/operator/bloc/operator_list_cubit.dart';
|
||||||
|
export 'src/operator/bloc/operator_overview_cubit.dart';
|
||||||
export 'src/operator/data/instance_operator_gateway.dart';
|
export 'src/operator/data/instance_operator_gateway.dart';
|
||||||
export 'src/operator/data/instance_operator_repository.dart';
|
export 'src/operator/data/instance_operator_repository.dart';
|
||||||
export 'src/operator/data/serverpod_instance_operator_gateway.dart';
|
export 'src/operator/data/serverpod_instance_operator_gateway.dart';
|
||||||
|
|
|
||||||
|
|
@ -314,6 +314,9 @@
|
||||||
"operatorAccounts": "Konten und Admins",
|
"operatorAccounts": "Konten und Admins",
|
||||||
"operatorWaitingCount": "{count, plural, =0{Niemand wartet auf Freigabe} =1{1 Konto wartet auf Freigabe} other{{count} Konten warten auf Freigabe}}",
|
"operatorWaitingCount": "{count, plural, =0{Niemand wartet auf Freigabe} =1{1 Konto wartet auf Freigabe} other{{count} Konten warten auf Freigabe}}",
|
||||||
"operatorWaitingTitle": "Wartend",
|
"operatorWaitingTitle": "Wartend",
|
||||||
|
"operatorOperatorsCount": "{count, plural, =1{1 Admin} other{{count} Admins}}",
|
||||||
|
"operatorAccountsTitle": "Konten",
|
||||||
|
"operatorAccountsCount": "{count, plural, =1{1 Konto} other{{count} Konten}}",
|
||||||
"operatorNobodyWaits": "Niemand wartet.",
|
"operatorNobodyWaits": "Niemand wartet.",
|
||||||
"operatorNoHandle": "Noch kein Handle",
|
"operatorNoHandle": "Noch kein Handle",
|
||||||
"operatorNewSince": "Neu, wartet seit {date}",
|
"operatorNewSince": "Neu, wartet seit {date}",
|
||||||
|
|
@ -331,7 +334,7 @@
|
||||||
"operatorAppointMessage": "Admins geben hier Konten frei, lehnen sie ab, sperren sie und ernennen Admins, so wie du.",
|
"operatorAppointMessage": "Admins geben hier Konten frei, lehnen sie ab, sperren sie und ernennen Admins, so wie du.",
|
||||||
"operatorAppointConfirm": "Ernennen",
|
"operatorAppointConfirm": "Ernennen",
|
||||||
"operatorHandle": "Handle",
|
"operatorHandle": "Handle",
|
||||||
"operatorFindTitle": "Ein Konto finden",
|
"operatorHandleSuggested": "Vorgeschlagen werden nur Handles, die es hier gibt.",
|
||||||
"operatorFoundNone": "Kein Handle beginnt so.",
|
"operatorFoundNone": "Kein Handle beginnt so.",
|
||||||
"operatorMadeOn": "Seit {date}",
|
"operatorMadeOn": "Seit {date}",
|
||||||
"operatorIsOperator": "Admin",
|
"operatorIsOperator": "Admin",
|
||||||
|
|
@ -350,27 +353,39 @@
|
||||||
"operatorInvalidLimit": "Eine Grenze kann nicht unter null liegen.",
|
"operatorInvalidLimit": "Eine Grenze kann nicht unter null liegen.",
|
||||||
"assistantGrantsTitle": "Assistenten",
|
"assistantGrantsTitle": "Assistenten",
|
||||||
"assistantGrantsNone": "Noch hat niemand einen Assistenten.",
|
"assistantGrantsNone": "Noch hat niemand einen Assistenten.",
|
||||||
|
"assistantSetCount": "{count, plural, =0{Keiner eingerichtet} =1{1 Assistent} other{{count} Assistenten}}",
|
||||||
|
"assistantPeopleCount": "{count, plural, =0{niemandem freigegeben} =1{1 Person} other{{count} Personen}}",
|
||||||
|
"assistantSetUpTitle": "Hier eingerichtet",
|
||||||
|
"assistantPeopleTitle": "Personen",
|
||||||
|
"assistantGranteesFoundNone": "Niemand mit Assistenten hat ein Handle, das so beginnt.",
|
||||||
|
"assistantUnpriced": "Ohne Preise: zählt nur Tokens",
|
||||||
|
"assistantEffort": "{effort, select, low{niedrig} medium{mittel} high{hoch} xhigh{sehr hoch} max{maximal} other{{effort}}}",
|
||||||
|
"assistantEffortName": "{effort, select, low{Niedrig} medium{Mittel} high{Hoch} xhigh{Sehr hoch} max{Maximal} other{{effort}}}",
|
||||||
|
"assistantEffortSetUp": "Denkaufwand {effort}, bis {max}",
|
||||||
|
"assistantEffortFixed": "Denkaufwand {effort}",
|
||||||
|
"assistantNameUnset": "{name} (nicht mehr eingerichtet)",
|
||||||
|
"assistantNotAgreedCount": "{count, plural, =1{1 noch ohne Zustimmung} other{{count} noch ohne Zustimmung}}",
|
||||||
"assistantGrant": "Assistenten freigeben",
|
"assistantGrant": "Assistenten freigeben",
|
||||||
"assistantGrantSave": "Freigeben",
|
"assistantGrantSave": "Freigeben",
|
||||||
"assistantGrantChange": "Grenzen ändern",
|
"assistantGrantChange": "Assistenten ändern",
|
||||||
"assistantGrantAssistant": "Assistent",
|
|
||||||
"assistantGrantDaily": "Grenze pro Tag",
|
|
||||||
"assistantGrantMonthly": "Grenze pro Monat",
|
|
||||||
"assistantGrantLimitHelper": "Leer für keine. Tage und Monate nach UTC.",
|
"assistantGrantLimitHelper": "Leer für keine. Tage und Monate nach UTC.",
|
||||||
|
"assistantGrantBudget": "Budget, für alle zusammen",
|
||||||
|
"assistantGrantMoneyDaily": "Geld pro Tag",
|
||||||
|
"assistantGrantMoneyMonthly": "Geld pro Monat",
|
||||||
|
"assistantGrantTokensDaily": "Tokens pro Tag",
|
||||||
|
"assistantGrantTokensMonthly": "Tokens pro Monat",
|
||||||
|
"assistantGrantMoneyNote": "Geld zählt die Assistenten mit Preisen, Tokens zählen alle. Was zuerst aufgebraucht ist, hält sie an.",
|
||||||
|
"assistantGrantReplaces": "{count, plural, =1{Die Freigabe ersetzt das Budget dieser Person:} other{Die Freigabe ersetzt die Budgets dieser Personen:}}",
|
||||||
|
"assistantGrantUnsetDropped": "Beim Speichern wird zurückgenommen, was nicht mehr eingerichtet ist: {names}.",
|
||||||
"assistantGrantLimitInvalid": "Keine Zahl, die das nimmt",
|
"assistantGrantLimitInvalid": "Keine Zahl, die das nimmt",
|
||||||
"assistantGrantNoHandle": "Kein Konto hat dieses Handle.",
|
|
||||||
"assistantGrantTokens": "Tokens",
|
"assistantGrantTokens": "Tokens",
|
||||||
"assistantGrantNote": "Vor der ersten Frage stimmen sie zu, was wohin geht. Du siehst, wie viel sie nutzen, nie, was sie fragen.",
|
"assistantGrantNote": "Vor der ersten Frage stimmen sie zu, was wohin geht. Assistenten, die sie schon haben, bleiben. Du siehst, wie viel sie nutzen, nie, was sie fragen.",
|
||||||
"assistantPerDay": "{amount} pro Tag",
|
"assistantPerDay": "{amount} pro Tag",
|
||||||
"assistantPerMonth": "{amount} pro Monat",
|
"assistantPerMonth": "{amount} pro Monat",
|
||||||
"assistantNoLimits": "Ohne Grenzen",
|
"assistantNoLimits": "Ohne Grenzen",
|
||||||
"assistantGrantAgreed": "Zugestimmt",
|
|
||||||
"assistantGrantNotAgreed": "Noch nicht zugestimmt",
|
|
||||||
"assistantGrantOutdated": "Die Preise haben sich geändert: Grenzen neu setzen",
|
|
||||||
"assistantGrantUnset": "Nicht mehr eingerichtet",
|
|
||||||
"assistantRevoke": "Zurücknehmen",
|
"assistantRevoke": "Zurücknehmen",
|
||||||
"assistantRevokeTitle": "{assistant} zurücknehmen?",
|
"assistantRevokeAllTitle": "Alle Assistenten zurücknehmen?",
|
||||||
"assistantRevokeMessage": "Sie können ihn nicht mehr fragen. Was sie genutzt haben, bleibt gezählt.",
|
"assistantRevokeMessage": "Sie können keinen mehr fragen, und ihr Budget entfällt. Was sie genutzt haben, bleibt gezählt.",
|
||||||
"assistantUseByMonth": "Nutzung nach Monat",
|
"assistantUseByMonth": "Nutzung nach Monat",
|
||||||
"assistantUseTitle": "Nutzung der Assistenten",
|
"assistantUseTitle": "Nutzung der Assistenten",
|
||||||
"assistantUseNone": "In diesem Monat nichts genutzt.",
|
"assistantUseNone": "In diesem Monat nichts genutzt.",
|
||||||
|
|
@ -381,8 +396,8 @@
|
||||||
"assistantModelAt": "{model} bei {host}",
|
"assistantModelAt": "{model} bei {host}",
|
||||||
"assistantLeftToday": "{left} von {limit} heute übrig",
|
"assistantLeftToday": "{left} von {limit} heute übrig",
|
||||||
"assistantLeftThisMonth": "{left} von {limit} diesen Monat übrig",
|
"assistantLeftThisMonth": "{left} von {limit} diesen Monat übrig",
|
||||||
|
"assistantYourBudget": "Dein Budget, für alle zusammen",
|
||||||
"assistantAgree": "Lesen und zustimmen",
|
"assistantAgree": "Lesen und zustimmen",
|
||||||
"assistantWaitsForLimits": "Wartet darauf, dass seine Grenzen neu gesetzt werden",
|
|
||||||
"assistantYourUse": "Deine Nutzung",
|
"assistantYourUse": "Deine Nutzung",
|
||||||
"assistantPrivacyPage": "Was auf {instance} wohin geht",
|
"assistantPrivacyPage": "Was auf {instance} wohin geht",
|
||||||
"assistantOwnTitle": "Mit deinem eigenen Schlüssel",
|
"assistantOwnTitle": "Mit deinem eigenen Schlüssel",
|
||||||
|
|
@ -415,9 +430,9 @@
|
||||||
"assistantConsentAgreedOn": "Du hast am {date} zugestimmt.",
|
"assistantConsentAgreedOn": "Du hast am {date} zugestimmt.",
|
||||||
"assistantProblemNotGranted": "Dieser Assistent ist für dich nicht mehr freigegeben.",
|
"assistantProblemNotGranted": "Dieser Assistent ist für dich nicht mehr freigegeben.",
|
||||||
"assistantProblemNotAgreed": "Stimm zuerst zu, was wohin geht.",
|
"assistantProblemNotAgreed": "Stimm zuerst zu, was wohin geht.",
|
||||||
|
"assistantProblemInvalidEffort": "Mit diesem Denkaufwand antwortet er nicht.",
|
||||||
"assistantProblemLimitReached": "Seine Grenze ist bis {time} erreicht.",
|
"assistantProblemLimitReached": "Seine Grenze ist bis {time} erreicht.",
|
||||||
"assistantProblemLimitReachedNow": "Seine Grenze ist vorerst erreicht.",
|
"assistantProblemLimitReachedNow": "Seine Grenze ist vorerst erreicht.",
|
||||||
"assistantProblemLimitOutdated": "Es wartet darauf, dass seine Grenzen neu gesetzt werden.",
|
|
||||||
"assistantProblemImagesNotRead": "Es liest keine Bilder.",
|
"assistantProblemImagesNotRead": "Es liest keine Bilder.",
|
||||||
"assistantProblemMalformed": "Diese Frage kann nicht gesendet werden.",
|
"assistantProblemMalformed": "Diese Frage kann nicht gesendet werden.",
|
||||||
"assistantProblemTooLarge": "Diese Frage ist zu lang oder enthält zu viele oder zu große Bilder.",
|
"assistantProblemTooLarge": "Diese Frage ist zu lang oder enthält zu viele oder zu große Bilder.",
|
||||||
|
|
|
||||||
|
|
@ -764,6 +764,28 @@
|
||||||
"@operatorWaitingTitle": {
|
"@operatorWaitingTitle": {
|
||||||
"description": "The card of accounts that wait for approval, new or suspended."
|
"description": "The card of accounts that wait for approval, new or suspended."
|
||||||
},
|
},
|
||||||
|
"operatorOperatorsCount": "{count, plural, =1{1 operator} other{{count} operators}}",
|
||||||
|
"@operatorOperatorsCount": {
|
||||||
|
"description": "How many operate the instance, in the overview's row.",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"operatorAccountsTitle": "Accounts",
|
||||||
|
"@operatorAccountsTitle": {
|
||||||
|
"description": "The overview's row leading to every account, and that screen's title."
|
||||||
|
},
|
||||||
|
"operatorAccountsCount": "{count, plural, =1{1 account} other{{count} accounts}}",
|
||||||
|
"@operatorAccountsCount": {
|
||||||
|
"description": "How many accounts the instance has.",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"operatorNobodyWaits": "Nobody waits.",
|
"operatorNobodyWaits": "Nobody waits.",
|
||||||
"@operatorNobodyWaits": {
|
"@operatorNobodyWaits": {
|
||||||
"description": "The waiting card, empty."
|
"description": "The waiting card, empty."
|
||||||
|
|
@ -842,9 +864,9 @@
|
||||||
"@operatorHandle": {
|
"@operatorHandle": {
|
||||||
"description": "The field for an account's handle."
|
"description": "The field for an account's handle."
|
||||||
},
|
},
|
||||||
"operatorFindTitle": "Find an account",
|
"operatorHandleSuggested": "Only handles there are here are suggested.",
|
||||||
"@operatorFindTitle": {
|
"@operatorHandleSuggested": {
|
||||||
"description": "The card searching accounts by the start of their handle."
|
"description": "Below a field suggesting handles as they are typed."
|
||||||
},
|
},
|
||||||
"operatorFoundNone": "No account's handle starts like this.",
|
"operatorFoundNone": "No account's handle starts like this.",
|
||||||
"@operatorFoundNone": {
|
"@operatorFoundNone": {
|
||||||
|
|
@ -923,7 +945,98 @@
|
||||||
"@assistantGrantsNone": {
|
"@assistantGrantsNone": {
|
||||||
"description": "No assistant is granted to anyone."
|
"description": "No assistant is granted to anyone."
|
||||||
},
|
},
|
||||||
"assistantGrant": "Grant an assistant",
|
"assistantSetCount": "{count, plural, =0{None set up} =1{1 assistant} other{{count} assistants}}",
|
||||||
|
"@assistantSetCount": {
|
||||||
|
"description": "How many assistants are set up, in the overview's row.",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assistantPeopleCount": "{count, plural, =0{nobody granted} =1{1 person} other{{count} people}}",
|
||||||
|
"@assistantPeopleCount": {
|
||||||
|
"description": "How many people have an assistant, after how many are set up.",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assistantSetUpTitle": "Set up here",
|
||||||
|
"@assistantSetUpTitle": {
|
||||||
|
"description": "The card of the assistants set up in deploy/assistants.env."
|
||||||
|
},
|
||||||
|
"assistantPeopleTitle": "People",
|
||||||
|
"@assistantPeopleTitle": {
|
||||||
|
"description": "The card, and the sheet section, of the people granted assistants."
|
||||||
|
},
|
||||||
|
"assistantGranteesFoundNone": "Nobody with an assistant has a handle starting like this.",
|
||||||
|
"@assistantGranteesFoundNone": {
|
||||||
|
"description": "No person granted one matches the start of a handle typed."
|
||||||
|
},
|
||||||
|
"assistantUnpriced": "No prices: counts its tokens only",
|
||||||
|
"@assistantUnpriced": {
|
||||||
|
"description": "An assistant set up without prices, which a budget counts in tokens alone."
|
||||||
|
},
|
||||||
|
"assistantEffort": "{effort, select, low{low} medium{medium} high{high} xhigh{extra high} max{max} other{{effort}}}",
|
||||||
|
"@assistantEffort": {
|
||||||
|
"description": "How much a model thinks before it answers, as a word in a sentence.",
|
||||||
|
"placeholders": {
|
||||||
|
"effort": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assistantEffortName": "{effort, select, low{Low} medium{Medium} high{High} xhigh{Extra high} max{Max} other{{effort}}}",
|
||||||
|
"@assistantEffortName": {
|
||||||
|
"description": "How much a model thinks before it answers, as a name standing alone, as in a menu.",
|
||||||
|
"placeholders": {
|
||||||
|
"effort": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assistantEffortSetUp": "Effort {effort}, up to {max}",
|
||||||
|
"@assistantEffortSetUp": {
|
||||||
|
"description": "An assistant's effort unless asked otherwise, and the highest a question may ask.",
|
||||||
|
"placeholders": {
|
||||||
|
"effort": {
|
||||||
|
"type": "String"
|
||||||
|
},
|
||||||
|
"max": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assistantEffortFixed": "Effort {effort}",
|
||||||
|
"@assistantEffortFixed": {
|
||||||
|
"description": "An assistant's effort, the only one it takes.",
|
||||||
|
"placeholders": {
|
||||||
|
"effort": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assistantNameUnset": "{name} (no longer set up)",
|
||||||
|
"@assistantNameUnset": {
|
||||||
|
"description": "An assistant granted that is no longer set up.",
|
||||||
|
"placeholders": {
|
||||||
|
"name": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assistantNotAgreedCount": "{count, plural, =1{1 not agreed to yet} other{{count} not agreed to yet}}",
|
||||||
|
"@assistantNotAgreedCount": {
|
||||||
|
"description": "How many of a person's assistants they haven't agreed to yet.",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assistantGrant": "Grant assistants",
|
||||||
"@assistantGrant": {
|
"@assistantGrant": {
|
||||||
"description": "Opens the sheet granting an assistant to an account."
|
"description": "Opens the sheet granting an assistant to an account."
|
||||||
},
|
},
|
||||||
|
|
@ -931,39 +1044,65 @@
|
||||||
"@assistantGrantSave": {
|
"@assistantGrantSave": {
|
||||||
"description": "The grant sheet's button."
|
"description": "The grant sheet's button."
|
||||||
},
|
},
|
||||||
"assistantGrantChange": "Change limits",
|
"assistantGrantChange": "Change assistants",
|
||||||
"@assistantGrantChange": {
|
"@assistantGrantChange": {
|
||||||
"description": "A grant's menu item, and the sheet's title, changing its limits."
|
"description": "The sheet changing a person's assistants and budget, and a person's menu item."
|
||||||
},
|
|
||||||
"assistantGrantAssistant": "Assistant",
|
|
||||||
"@assistantGrantAssistant": {
|
|
||||||
"description": "Above the choice of assistant in the grant sheet."
|
|
||||||
},
|
|
||||||
"assistantGrantDaily": "Limit a day",
|
|
||||||
"@assistantGrantDaily": {
|
|
||||||
"description": "The daily limit field."
|
|
||||||
},
|
|
||||||
"assistantGrantMonthly": "Limit a month",
|
|
||||||
"@assistantGrantMonthly": {
|
|
||||||
"description": "The monthly limit field."
|
|
||||||
},
|
},
|
||||||
"assistantGrantLimitHelper": "Empty for none. Days and months are UTC's.",
|
"assistantGrantLimitHelper": "Empty for none. Days and months are UTC's.",
|
||||||
"@assistantGrantLimitHelper": {
|
"@assistantGrantLimitHelper": {
|
||||||
"description": "Under the limit fields: the instance counts by UTC days and months (D42)."
|
"description": "Under the limit fields: the instance counts by UTC days and months (D42)."
|
||||||
},
|
},
|
||||||
|
"assistantGrantBudget": "Budget, for all of them together",
|
||||||
|
"@assistantGrantBudget": {
|
||||||
|
"description": "The sheet's section of a person's budget."
|
||||||
|
},
|
||||||
|
"assistantGrantMoneyDaily": "Money a day",
|
||||||
|
"@assistantGrantMoneyDaily": {
|
||||||
|
"description": "A budget field."
|
||||||
|
},
|
||||||
|
"assistantGrantMoneyMonthly": "Money a month",
|
||||||
|
"@assistantGrantMoneyMonthly": {
|
||||||
|
"description": "A budget field."
|
||||||
|
},
|
||||||
|
"assistantGrantTokensDaily": "Tokens a day",
|
||||||
|
"@assistantGrantTokensDaily": {
|
||||||
|
"description": "A budget field."
|
||||||
|
},
|
||||||
|
"assistantGrantTokensMonthly": "Tokens a month",
|
||||||
|
"@assistantGrantTokensMonthly": {
|
||||||
|
"description": "A budget field."
|
||||||
|
},
|
||||||
|
"assistantGrantMoneyNote": "Money counts the assistants with prices, tokens count every one. Whichever runs out first stops them.",
|
||||||
|
"@assistantGrantMoneyNote": {
|
||||||
|
"description": "Below the budget fields."
|
||||||
|
},
|
||||||
|
"assistantGrantReplaces": "{count, plural, =1{Granting replaces the budget this person has:} other{Granting replaces the budgets these people have:}}",
|
||||||
|
"@assistantGrantReplaces": {
|
||||||
|
"description": "Before the people whose budget a grant replaces, each with theirs.",
|
||||||
|
"placeholders": {
|
||||||
|
"count": {
|
||||||
|
"type": "int"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assistantGrantUnsetDropped": "Saving takes back what is no longer set up: {names}.",
|
||||||
|
"@assistantGrantUnsetDropped": {
|
||||||
|
"description": "Changing a person's assistants drops those no longer set up.",
|
||||||
|
"placeholders": {
|
||||||
|
"names": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"assistantGrantLimitInvalid": "Not a number this takes",
|
"assistantGrantLimitInvalid": "Not a number this takes",
|
||||||
"@assistantGrantLimitInvalid": {
|
"@assistantGrantLimitInvalid": {
|
||||||
"description": "A limit typed that is no amount or number of tokens."
|
"description": "A limit typed that is no amount or number of tokens."
|
||||||
},
|
},
|
||||||
"assistantGrantNoHandle": "No account has this handle.",
|
|
||||||
"@assistantGrantNoHandle": {
|
|
||||||
"description": "The grant sheet: the handle typed is nobody's."
|
|
||||||
},
|
|
||||||
"assistantGrantTokens": "tokens",
|
"assistantGrantTokens": "tokens",
|
||||||
"@assistantGrantTokens": {
|
"@assistantGrantTokens": {
|
||||||
"description": "After a limit in tokens, for an assistant without prices."
|
"description": "After a limit in tokens, for an assistant without prices."
|
||||||
},
|
},
|
||||||
"assistantGrantNote": "They agree once to what goes where before they ask it. You see how much they use, never what they ask.",
|
"assistantGrantNote": "They agree once to what goes where before they ask one. Assistants they have already stay. You see how much they use, never what they ask.",
|
||||||
"@assistantGrantNote": {
|
"@assistantGrantNote": {
|
||||||
"description": "In the grant sheet: consent (D40) and what the operator sees (D3)."
|
"description": "In the grant sheet: consent (D40) and what the operator sees (D3)."
|
||||||
},
|
},
|
||||||
|
|
@ -989,36 +1128,15 @@
|
||||||
"@assistantNoLimits": {
|
"@assistantNoLimits": {
|
||||||
"description": "A grant without limits."
|
"description": "A grant without limits."
|
||||||
},
|
},
|
||||||
"assistantGrantAgreed": "Agreed",
|
|
||||||
"@assistantGrantAgreed": {
|
|
||||||
"description": "A grant whose person agreed to what goes where."
|
|
||||||
},
|
|
||||||
"assistantGrantNotAgreed": "Not agreed yet",
|
|
||||||
"@assistantGrantNotAgreed": {
|
|
||||||
"description": "A grant whose person hasn't agreed yet."
|
|
||||||
},
|
|
||||||
"assistantGrantOutdated": "Its prices changed: set its limits again",
|
|
||||||
"@assistantGrantOutdated": {
|
|
||||||
"description": "A grant whose limits are in another unit than the pricing now; it answers nothing until set again."
|
|
||||||
},
|
|
||||||
"assistantGrantUnset": "No longer set up",
|
|
||||||
"@assistantGrantUnset": {
|
|
||||||
"description": "A grant of an assistant taken out of deploy/assistants.env."
|
|
||||||
},
|
|
||||||
"assistantRevoke": "Take back",
|
"assistantRevoke": "Take back",
|
||||||
"@assistantRevoke": {
|
"@assistantRevoke": {
|
||||||
"description": "A grant's menu item."
|
"description": "A grant's menu item."
|
||||||
},
|
},
|
||||||
"assistantRevokeTitle": "Take back {assistant}?",
|
"assistantRevokeAllTitle": "Take back every assistant?",
|
||||||
"@assistantRevokeTitle": {
|
"@assistantRevokeAllTitle": {
|
||||||
"description": "Asks before taking a grant back.",
|
"description": "Asks before taking every assistant back from a person."
|
||||||
"placeholders": {
|
|
||||||
"assistant": {
|
|
||||||
"type": "String"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"assistantRevokeMessage": "They can no longer ask it. What they used stays counted.",
|
"assistantRevokeMessage": "They can no longer ask any of them, and their budget goes. What they used stays counted.",
|
||||||
"@assistantRevokeMessage": {
|
"@assistantRevokeMessage": {
|
||||||
"description": "Under the question taking a grant back."
|
"description": "Under the question taking a grant back."
|
||||||
},
|
},
|
||||||
|
|
@ -1096,14 +1214,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"assistantYourBudget": "Your budget, for all of them",
|
||||||
|
"@assistantYourBudget": {
|
||||||
|
"description": "The person's budget for all their assistants."
|
||||||
|
},
|
||||||
"assistantAgree": "Read and agree",
|
"assistantAgree": "Read and agree",
|
||||||
"@assistantAgree": {
|
"@assistantAgree": {
|
||||||
"description": "Opens the consent sheet of an assistant not agreed to yet."
|
"description": "Opens the consent sheet of an assistant not agreed to yet."
|
||||||
},
|
},
|
||||||
"assistantWaitsForLimits": "Waits for its limits to be set again",
|
|
||||||
"@assistantWaitsForLimits": {
|
|
||||||
"description": "An assistant whose limits are outdated: it answers nothing until the operator sets them again."
|
|
||||||
},
|
|
||||||
"assistantYourUse": "Your use",
|
"assistantYourUse": "Your use",
|
||||||
"@assistantYourUse": {
|
"@assistantYourUse": {
|
||||||
"description": "Opens the person's own figures."
|
"description": "Opens the person's own figures."
|
||||||
|
|
@ -1276,6 +1394,10 @@
|
||||||
"@assistantProblemNotAgreed": {
|
"@assistantProblemNotAgreed": {
|
||||||
"description": "notAgreed."
|
"description": "notAgreed."
|
||||||
},
|
},
|
||||||
|
"assistantProblemInvalidEffort": "It doesn't think at this effort.",
|
||||||
|
"@assistantProblemInvalidEffort": {
|
||||||
|
"description": "A question asked an effort its assistant does not take."
|
||||||
|
},
|
||||||
"assistantProblemLimitReached": "Its limit is reached until {time}.",
|
"assistantProblemLimitReached": "Its limit is reached until {time}.",
|
||||||
"@assistantProblemLimitReached": {
|
"@assistantProblemLimitReached": {
|
||||||
"description": "limitReached, with when it resets.",
|
"description": "limitReached, with when it resets.",
|
||||||
|
|
@ -1289,10 +1411,6 @@
|
||||||
"@assistantProblemLimitReachedNow": {
|
"@assistantProblemLimitReachedNow": {
|
||||||
"description": "Refused: a limit is reached, the instance not saying until when."
|
"description": "Refused: a limit is reached, the instance not saying until when."
|
||||||
},
|
},
|
||||||
"assistantProblemLimitOutdated": "It waits for its limits to be set again.",
|
|
||||||
"@assistantProblemLimitOutdated": {
|
|
||||||
"description": "limitOutdated."
|
|
||||||
},
|
|
||||||
"assistantProblemImagesNotRead": "It reads no images.",
|
"assistantProblemImagesNotRead": "It reads no images.",
|
||||||
"@assistantProblemImagesNotRead": {
|
"@assistantProblemImagesNotRead": {
|
||||||
"description": "imagesNotRead."
|
"description": "imagesNotRead."
|
||||||
|
|
|
||||||
|
|
@ -1987,6 +1987,24 @@ abstract class HalkyonAppLocalizations {
|
||||||
/// **'Waiting'**
|
/// **'Waiting'**
|
||||||
String get operatorWaitingTitle;
|
String get operatorWaitingTitle;
|
||||||
|
|
||||||
|
/// How many operate the instance, in the overview's row.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{count, plural, =1{1 operator} other{{count} operators}}'**
|
||||||
|
String operatorOperatorsCount(int count);
|
||||||
|
|
||||||
|
/// The overview's row leading to every account, and that screen's title.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Accounts'**
|
||||||
|
String get operatorAccountsTitle;
|
||||||
|
|
||||||
|
/// How many accounts the instance has.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{count, plural, =1{1 account} other{{count} accounts}}'**
|
||||||
|
String operatorAccountsCount(int count);
|
||||||
|
|
||||||
/// The waiting card, empty.
|
/// The waiting card, empty.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -2089,11 +2107,11 @@ abstract class HalkyonAppLocalizations {
|
||||||
/// **'Handle'**
|
/// **'Handle'**
|
||||||
String get operatorHandle;
|
String get operatorHandle;
|
||||||
|
|
||||||
/// The card searching accounts by the start of their handle.
|
/// Below a field suggesting handles as they are typed.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Find an account'**
|
/// **'Only handles there are here are suggested.'**
|
||||||
String get operatorFindTitle;
|
String get operatorHandleSuggested;
|
||||||
|
|
||||||
/// A search that found nothing.
|
/// A search that found nothing.
|
||||||
///
|
///
|
||||||
|
|
@ -2203,10 +2221,82 @@ abstract class HalkyonAppLocalizations {
|
||||||
/// **'Nobody has an assistant yet.'**
|
/// **'Nobody has an assistant yet.'**
|
||||||
String get assistantGrantsNone;
|
String get assistantGrantsNone;
|
||||||
|
|
||||||
|
/// How many assistants are set up, in the overview's row.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{count, plural, =0{None set up} =1{1 assistant} other{{count} assistants}}'**
|
||||||
|
String assistantSetCount(int count);
|
||||||
|
|
||||||
|
/// How many people have an assistant, after how many are set up.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{count, plural, =0{nobody granted} =1{1 person} other{{count} people}}'**
|
||||||
|
String assistantPeopleCount(int count);
|
||||||
|
|
||||||
|
/// The card of the assistants set up in deploy/assistants.env.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Set up here'**
|
||||||
|
String get assistantSetUpTitle;
|
||||||
|
|
||||||
|
/// The card, and the sheet section, of the people granted assistants.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'People'**
|
||||||
|
String get assistantPeopleTitle;
|
||||||
|
|
||||||
|
/// No person granted one matches the start of a handle typed.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Nobody with an assistant has a handle starting like this.'**
|
||||||
|
String get assistantGranteesFoundNone;
|
||||||
|
|
||||||
|
/// An assistant set up without prices, which a budget counts in tokens alone.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'No prices: counts its tokens only'**
|
||||||
|
String get assistantUnpriced;
|
||||||
|
|
||||||
|
/// How much a model thinks before it answers, as a word in a sentence.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{effort, select, low{low} medium{medium} high{high} xhigh{extra high} max{max} other{{effort}}}'**
|
||||||
|
String assistantEffort(String effort);
|
||||||
|
|
||||||
|
/// How much a model thinks before it answers, as a name standing alone, as in a menu.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{effort, select, low{Low} medium{Medium} high{High} xhigh{Extra high} max{Max} other{{effort}}}'**
|
||||||
|
String assistantEffortName(String effort);
|
||||||
|
|
||||||
|
/// An assistant's effort unless asked otherwise, and the highest a question may ask.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Effort {effort}, up to {max}'**
|
||||||
|
String assistantEffortSetUp(String effort, String max);
|
||||||
|
|
||||||
|
/// An assistant's effort, the only one it takes.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Effort {effort}'**
|
||||||
|
String assistantEffortFixed(String effort);
|
||||||
|
|
||||||
|
/// An assistant granted that is no longer set up.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{name} (no longer set up)'**
|
||||||
|
String assistantNameUnset(String name);
|
||||||
|
|
||||||
|
/// How many of a person's assistants they haven't agreed to yet.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{count, plural, =1{1 not agreed to yet} other{{count} not agreed to yet}}'**
|
||||||
|
String assistantNotAgreedCount(int count);
|
||||||
|
|
||||||
/// Opens the sheet granting an assistant to an account.
|
/// Opens the sheet granting an assistant to an account.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Grant an assistant'**
|
/// **'Grant assistants'**
|
||||||
String get assistantGrant;
|
String get assistantGrant;
|
||||||
|
|
||||||
/// The grant sheet's button.
|
/// The grant sheet's button.
|
||||||
|
|
@ -2215,48 +2305,72 @@ abstract class HalkyonAppLocalizations {
|
||||||
/// **'Grant'**
|
/// **'Grant'**
|
||||||
String get assistantGrantSave;
|
String get assistantGrantSave;
|
||||||
|
|
||||||
/// A grant's menu item, and the sheet's title, changing its limits.
|
/// The sheet changing a person's assistants and budget, and a person's menu item.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Change limits'**
|
/// **'Change assistants'**
|
||||||
String get assistantGrantChange;
|
String get assistantGrantChange;
|
||||||
|
|
||||||
/// Above the choice of assistant in the grant sheet.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Assistant'**
|
|
||||||
String get assistantGrantAssistant;
|
|
||||||
|
|
||||||
/// The daily limit field.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Limit a day'**
|
|
||||||
String get assistantGrantDaily;
|
|
||||||
|
|
||||||
/// The monthly limit field.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Limit a month'**
|
|
||||||
String get assistantGrantMonthly;
|
|
||||||
|
|
||||||
/// Under the limit fields: the instance counts by UTC days and months (D42).
|
/// Under the limit fields: the instance counts by UTC days and months (D42).
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Empty for none. Days and months are UTC\'s.'**
|
/// **'Empty for none. Days and months are UTC\'s.'**
|
||||||
String get assistantGrantLimitHelper;
|
String get assistantGrantLimitHelper;
|
||||||
|
|
||||||
|
/// The sheet's section of a person's budget.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Budget, for all of them together'**
|
||||||
|
String get assistantGrantBudget;
|
||||||
|
|
||||||
|
/// A budget field.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Money a day'**
|
||||||
|
String get assistantGrantMoneyDaily;
|
||||||
|
|
||||||
|
/// A budget field.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Money a month'**
|
||||||
|
String get assistantGrantMoneyMonthly;
|
||||||
|
|
||||||
|
/// A budget field.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Tokens a day'**
|
||||||
|
String get assistantGrantTokensDaily;
|
||||||
|
|
||||||
|
/// A budget field.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Tokens a month'**
|
||||||
|
String get assistantGrantTokensMonthly;
|
||||||
|
|
||||||
|
/// Below the budget fields.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Money counts the assistants with prices, tokens count every one. Whichever runs out first stops them.'**
|
||||||
|
String get assistantGrantMoneyNote;
|
||||||
|
|
||||||
|
/// Before the people whose budget a grant replaces, each with theirs.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'{count, plural, =1{Granting replaces the budget this person has:} other{Granting replaces the budgets these people have:}}'**
|
||||||
|
String assistantGrantReplaces(int count);
|
||||||
|
|
||||||
|
/// Changing a person's assistants drops those no longer set up.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Saving takes back what is no longer set up: {names}.'**
|
||||||
|
String assistantGrantUnsetDropped(String names);
|
||||||
|
|
||||||
/// A limit typed that is no amount or number of tokens.
|
/// A limit typed that is no amount or number of tokens.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Not a number this takes'**
|
/// **'Not a number this takes'**
|
||||||
String get assistantGrantLimitInvalid;
|
String get assistantGrantLimitInvalid;
|
||||||
|
|
||||||
/// The grant sheet: the handle typed is nobody's.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'No account has this handle.'**
|
|
||||||
String get assistantGrantNoHandle;
|
|
||||||
|
|
||||||
/// After a limit in tokens, for an assistant without prices.
|
/// After a limit in tokens, for an assistant without prices.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -2266,7 +2380,7 @@ abstract class HalkyonAppLocalizations {
|
||||||
/// In the grant sheet: consent (D40) and what the operator sees (D3).
|
/// In the grant sheet: consent (D40) and what the operator sees (D3).
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'They agree once to what goes where before they ask it. You see how much they use, never what they ask.'**
|
/// **'They agree once to what goes where before they ask one. Assistants they have already stay. You see how much they use, never what they ask.'**
|
||||||
String get assistantGrantNote;
|
String get assistantGrantNote;
|
||||||
|
|
||||||
/// A daily limit.
|
/// A daily limit.
|
||||||
|
|
@ -2287,46 +2401,22 @@ abstract class HalkyonAppLocalizations {
|
||||||
/// **'No limits'**
|
/// **'No limits'**
|
||||||
String get assistantNoLimits;
|
String get assistantNoLimits;
|
||||||
|
|
||||||
/// A grant whose person agreed to what goes where.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Agreed'**
|
|
||||||
String get assistantGrantAgreed;
|
|
||||||
|
|
||||||
/// A grant whose person hasn't agreed yet.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Not agreed yet'**
|
|
||||||
String get assistantGrantNotAgreed;
|
|
||||||
|
|
||||||
/// A grant whose limits are in another unit than the pricing now; it answers nothing until set again.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Its prices changed: set its limits again'**
|
|
||||||
String get assistantGrantOutdated;
|
|
||||||
|
|
||||||
/// A grant of an assistant taken out of deploy/assistants.env.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'No longer set up'**
|
|
||||||
String get assistantGrantUnset;
|
|
||||||
|
|
||||||
/// A grant's menu item.
|
/// A grant's menu item.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Take back'**
|
/// **'Take back'**
|
||||||
String get assistantRevoke;
|
String get assistantRevoke;
|
||||||
|
|
||||||
/// Asks before taking a grant back.
|
/// Asks before taking every assistant back from a person.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Take back {assistant}?'**
|
/// **'Take back every assistant?'**
|
||||||
String assistantRevokeTitle(String assistant);
|
String get assistantRevokeAllTitle;
|
||||||
|
|
||||||
/// Under the question taking a grant back.
|
/// Under the question taking a grant back.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'They can no longer ask it. What they used stays counted.'**
|
/// **'They can no longer ask any of them, and their budget goes. What they used stays counted.'**
|
||||||
String get assistantRevokeMessage;
|
String get assistantRevokeMessage;
|
||||||
|
|
||||||
/// Opens everyone's figures (D3).
|
/// Opens everyone's figures (D3).
|
||||||
|
|
@ -2389,18 +2479,18 @@ abstract class HalkyonAppLocalizations {
|
||||||
/// **'{left} of {limit} left this month'**
|
/// **'{left} of {limit} left this month'**
|
||||||
String assistantLeftThisMonth(String left, String limit);
|
String assistantLeftThisMonth(String left, String limit);
|
||||||
|
|
||||||
|
/// The person's budget for all their assistants.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Your budget, for all of them'**
|
||||||
|
String get assistantYourBudget;
|
||||||
|
|
||||||
/// Opens the consent sheet of an assistant not agreed to yet.
|
/// Opens the consent sheet of an assistant not agreed to yet.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Read and agree'**
|
/// **'Read and agree'**
|
||||||
String get assistantAgree;
|
String get assistantAgree;
|
||||||
|
|
||||||
/// An assistant whose limits are outdated: it answers nothing until the operator sets them again.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'Waits for its limits to be set again'**
|
|
||||||
String get assistantWaitsForLimits;
|
|
||||||
|
|
||||||
/// Opens the person's own figures.
|
/// Opens the person's own figures.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -2593,6 +2683,12 @@ abstract class HalkyonAppLocalizations {
|
||||||
/// **'Agree to what goes where first.'**
|
/// **'Agree to what goes where first.'**
|
||||||
String get assistantProblemNotAgreed;
|
String get assistantProblemNotAgreed;
|
||||||
|
|
||||||
|
/// A question asked an effort its assistant does not take.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'It doesn\'t think at this effort.'**
|
||||||
|
String get assistantProblemInvalidEffort;
|
||||||
|
|
||||||
/// limitReached, with when it resets.
|
/// limitReached, with when it resets.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
@ -2605,12 +2701,6 @@ abstract class HalkyonAppLocalizations {
|
||||||
/// **'Its limit is reached for now.'**
|
/// **'Its limit is reached for now.'**
|
||||||
String get assistantProblemLimitReachedNow;
|
String get assistantProblemLimitReachedNow;
|
||||||
|
|
||||||
/// limitOutdated.
|
|
||||||
///
|
|
||||||
/// In en, this message translates to:
|
|
||||||
/// **'It waits for its limits to be set again.'**
|
|
||||||
String get assistantProblemLimitOutdated;
|
|
||||||
|
|
||||||
/// imagesNotRead.
|
/// imagesNotRead.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|
|
||||||
|
|
@ -1194,6 +1194,31 @@ class HalkyonAppLocalizationsDe extends HalkyonAppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get operatorWaitingTitle => 'Wartend';
|
String get operatorWaitingTitle => 'Wartend';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String operatorOperatorsCount(int count) {
|
||||||
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
|
count,
|
||||||
|
locale: localeName,
|
||||||
|
other: '$count Admins',
|
||||||
|
one: '1 Admin',
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get operatorAccountsTitle => 'Konten';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String operatorAccountsCount(int count) {
|
||||||
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
|
count,
|
||||||
|
locale: localeName,
|
||||||
|
other: '$count Konten',
|
||||||
|
one: '1 Konto',
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get operatorNobodyWaits => 'Niemand wartet.';
|
String get operatorNobodyWaits => 'Niemand wartet.';
|
||||||
|
|
||||||
|
|
@ -1254,7 +1279,8 @@ class HalkyonAppLocalizationsDe extends HalkyonAppLocalizations {
|
||||||
String get operatorHandle => 'Handle';
|
String get operatorHandle => 'Handle';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get operatorFindTitle => 'Ein Konto finden';
|
String get operatorHandleSuggested =>
|
||||||
|
'Vorgeschlagen werden nur Handles, die es hier gibt.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get operatorFoundNone => 'Kein Handle beginnt so.';
|
String get operatorFoundNone => 'Kein Handle beginnt so.';
|
||||||
|
|
@ -1319,6 +1345,101 @@ class HalkyonAppLocalizationsDe extends HalkyonAppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get assistantGrantsNone => 'Noch hat niemand einen Assistenten.';
|
String get assistantGrantsNone => 'Noch hat niemand einen Assistenten.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantSetCount(int count) {
|
||||||
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
|
count,
|
||||||
|
locale: localeName,
|
||||||
|
other: '$count Assistenten',
|
||||||
|
one: '1 Assistent',
|
||||||
|
zero: 'Keiner eingerichtet',
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantPeopleCount(int count) {
|
||||||
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
|
count,
|
||||||
|
locale: localeName,
|
||||||
|
other: '$count Personen',
|
||||||
|
one: '1 Person',
|
||||||
|
zero: 'niemandem freigegeben',
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantSetUpTitle => 'Hier eingerichtet';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantPeopleTitle => 'Personen';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGranteesFoundNone =>
|
||||||
|
'Niemand mit Assistenten hat ein Handle, das so beginnt.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantUnpriced => 'Ohne Preise: zählt nur Tokens';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantEffort(String effort) {
|
||||||
|
String _temp0 = intl.Intl.selectLogic(
|
||||||
|
effort,
|
||||||
|
{
|
||||||
|
'low': 'niedrig',
|
||||||
|
'medium': 'mittel',
|
||||||
|
'high': 'hoch',
|
||||||
|
'xhigh': 'sehr hoch',
|
||||||
|
'max': 'maximal',
|
||||||
|
'other': '$effort',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantEffortName(String effort) {
|
||||||
|
String _temp0 = intl.Intl.selectLogic(
|
||||||
|
effort,
|
||||||
|
{
|
||||||
|
'low': 'Niedrig',
|
||||||
|
'medium': 'Mittel',
|
||||||
|
'high': 'Hoch',
|
||||||
|
'xhigh': 'Sehr hoch',
|
||||||
|
'max': 'Maximal',
|
||||||
|
'other': '$effort',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantEffortSetUp(String effort, String max) {
|
||||||
|
return 'Denkaufwand $effort, bis $max';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantEffortFixed(String effort) {
|
||||||
|
return 'Denkaufwand $effort';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantNameUnset(String name) {
|
||||||
|
return '$name (nicht mehr eingerichtet)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantNotAgreedCount(int count) {
|
||||||
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
|
count,
|
||||||
|
locale: localeName,
|
||||||
|
other: '$count noch ohne Zustimmung',
|
||||||
|
one: '1 noch ohne Zustimmung',
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrant => 'Assistenten freigeben';
|
String get assistantGrant => 'Assistenten freigeben';
|
||||||
|
|
||||||
|
|
@ -1326,33 +1447,56 @@ class HalkyonAppLocalizationsDe extends HalkyonAppLocalizations {
|
||||||
String get assistantGrantSave => 'Freigeben';
|
String get assistantGrantSave => 'Freigeben';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantChange => 'Grenzen ändern';
|
String get assistantGrantChange => 'Assistenten ändern';
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantAssistant => 'Assistent';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantDaily => 'Grenze pro Tag';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantMonthly => 'Grenze pro Monat';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantLimitHelper =>
|
String get assistantGrantLimitHelper =>
|
||||||
'Leer für keine. Tage und Monate nach UTC.';
|
'Leer für keine. Tage und Monate nach UTC.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantLimitInvalid => 'Keine Zahl, die das nimmt';
|
String get assistantGrantBudget => 'Budget, für alle zusammen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantNoHandle => 'Kein Konto hat dieses Handle.';
|
String get assistantGrantMoneyDaily => 'Geld pro Tag';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGrantMoneyMonthly => 'Geld pro Monat';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGrantTokensDaily => 'Tokens pro Tag';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGrantTokensMonthly => 'Tokens pro Monat';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGrantMoneyNote =>
|
||||||
|
'Geld zählt die Assistenten mit Preisen, Tokens zählen alle. Was zuerst aufgebraucht ist, hält sie an.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantGrantReplaces(int count) {
|
||||||
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
|
count,
|
||||||
|
locale: localeName,
|
||||||
|
other: 'Die Freigabe ersetzt die Budgets dieser Personen:',
|
||||||
|
one: 'Die Freigabe ersetzt das Budget dieser Person:',
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantGrantUnsetDropped(String names) {
|
||||||
|
return 'Beim Speichern wird zurückgenommen, was nicht mehr eingerichtet ist: $names.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGrantLimitInvalid => 'Keine Zahl, die das nimmt';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantTokens => 'Tokens';
|
String get assistantGrantTokens => 'Tokens';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantNote =>
|
String get assistantGrantNote =>
|
||||||
'Vor der ersten Frage stimmen sie zu, was wohin geht. Du siehst, wie viel sie nutzen, nie, was sie fragen.';
|
'Vor der ersten Frage stimmen sie zu, was wohin geht. Assistenten, die sie schon haben, bleiben. Du siehst, wie viel sie nutzen, nie, was sie fragen.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String assistantPerDay(String amount) {
|
String assistantPerDay(String amount) {
|
||||||
|
|
@ -1367,30 +1511,15 @@ class HalkyonAppLocalizationsDe extends HalkyonAppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get assistantNoLimits => 'Ohne Grenzen';
|
String get assistantNoLimits => 'Ohne Grenzen';
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantAgreed => 'Zugestimmt';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantNotAgreed => 'Noch nicht zugestimmt';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantOutdated =>
|
|
||||||
'Die Preise haben sich geändert: Grenzen neu setzen';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantUnset => 'Nicht mehr eingerichtet';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantRevoke => 'Zurücknehmen';
|
String get assistantRevoke => 'Zurücknehmen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String assistantRevokeTitle(String assistant) {
|
String get assistantRevokeAllTitle => 'Alle Assistenten zurücknehmen?';
|
||||||
return '$assistant zurücknehmen?';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantRevokeMessage =>
|
String get assistantRevokeMessage =>
|
||||||
'Sie können ihn nicht mehr fragen. Was sie genutzt haben, bleibt gezählt.';
|
'Sie können keinen mehr fragen, und ihr Budget entfällt. Was sie genutzt haben, bleibt gezählt.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantUseByMonth => 'Nutzung nach Monat';
|
String get assistantUseByMonth => 'Nutzung nach Monat';
|
||||||
|
|
@ -1440,11 +1569,10 @@ class HalkyonAppLocalizationsDe extends HalkyonAppLocalizations {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantAgree => 'Lesen und zustimmen';
|
String get assistantYourBudget => 'Dein Budget, für alle zusammen';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantWaitsForLimits =>
|
String get assistantAgree => 'Lesen und zustimmen';
|
||||||
'Wartet darauf, dass seine Grenzen neu gesetzt werden';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantYourUse => 'Deine Nutzung';
|
String get assistantYourUse => 'Deine Nutzung';
|
||||||
|
|
@ -1568,6 +1696,10 @@ class HalkyonAppLocalizationsDe extends HalkyonAppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get assistantProblemNotAgreed => 'Stimm zuerst zu, was wohin geht.';
|
String get assistantProblemNotAgreed => 'Stimm zuerst zu, was wohin geht.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantProblemInvalidEffort =>
|
||||||
|
'Mit diesem Denkaufwand antwortet er nicht.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String assistantProblemLimitReached(String time) {
|
String assistantProblemLimitReached(String time) {
|
||||||
return 'Seine Grenze ist bis $time erreicht.';
|
return 'Seine Grenze ist bis $time erreicht.';
|
||||||
|
|
@ -1577,10 +1709,6 @@ class HalkyonAppLocalizationsDe extends HalkyonAppLocalizations {
|
||||||
String get assistantProblemLimitReachedNow =>
|
String get assistantProblemLimitReachedNow =>
|
||||||
'Seine Grenze ist vorerst erreicht.';
|
'Seine Grenze ist vorerst erreicht.';
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantProblemLimitOutdated =>
|
|
||||||
'Es wartet darauf, dass seine Grenzen neu gesetzt werden.';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantProblemImagesNotRead => 'Es liest keine Bilder.';
|
String get assistantProblemImagesNotRead => 'Es liest keine Bilder.';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1173,6 +1173,31 @@ class HalkyonAppLocalizationsEn extends HalkyonAppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get operatorWaitingTitle => 'Waiting';
|
String get operatorWaitingTitle => 'Waiting';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String operatorOperatorsCount(int count) {
|
||||||
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
|
count,
|
||||||
|
locale: localeName,
|
||||||
|
other: '$count operators',
|
||||||
|
one: '1 operator',
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get operatorAccountsTitle => 'Accounts';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String operatorAccountsCount(int count) {
|
||||||
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
|
count,
|
||||||
|
locale: localeName,
|
||||||
|
other: '$count accounts',
|
||||||
|
one: '1 account',
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get operatorNobodyWaits => 'Nobody waits.';
|
String get operatorNobodyWaits => 'Nobody waits.';
|
||||||
|
|
||||||
|
|
@ -1233,7 +1258,8 @@ class HalkyonAppLocalizationsEn extends HalkyonAppLocalizations {
|
||||||
String get operatorHandle => 'Handle';
|
String get operatorHandle => 'Handle';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get operatorFindTitle => 'Find an account';
|
String get operatorHandleSuggested =>
|
||||||
|
'Only handles there are here are suggested.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get operatorFoundNone => 'No account\'s handle starts like this.';
|
String get operatorFoundNone => 'No account\'s handle starts like this.';
|
||||||
|
|
@ -1298,39 +1324,157 @@ class HalkyonAppLocalizationsEn extends HalkyonAppLocalizations {
|
||||||
String get assistantGrantsNone => 'Nobody has an assistant yet.';
|
String get assistantGrantsNone => 'Nobody has an assistant yet.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrant => 'Grant an assistant';
|
String assistantSetCount(int count) {
|
||||||
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
|
count,
|
||||||
|
locale: localeName,
|
||||||
|
other: '$count assistants',
|
||||||
|
one: '1 assistant',
|
||||||
|
zero: 'None set up',
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantPeopleCount(int count) {
|
||||||
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
|
count,
|
||||||
|
locale: localeName,
|
||||||
|
other: '$count people',
|
||||||
|
one: '1 person',
|
||||||
|
zero: 'nobody granted',
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantSetUpTitle => 'Set up here';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantPeopleTitle => 'People';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGranteesFoundNone =>
|
||||||
|
'Nobody with an assistant has a handle starting like this.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantUnpriced => 'No prices: counts its tokens only';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantEffort(String effort) {
|
||||||
|
String _temp0 = intl.Intl.selectLogic(
|
||||||
|
effort,
|
||||||
|
{
|
||||||
|
'low': 'low',
|
||||||
|
'medium': 'medium',
|
||||||
|
'high': 'high',
|
||||||
|
'xhigh': 'extra high',
|
||||||
|
'max': 'max',
|
||||||
|
'other': '$effort',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantEffortName(String effort) {
|
||||||
|
String _temp0 = intl.Intl.selectLogic(
|
||||||
|
effort,
|
||||||
|
{
|
||||||
|
'low': 'Low',
|
||||||
|
'medium': 'Medium',
|
||||||
|
'high': 'High',
|
||||||
|
'xhigh': 'Extra high',
|
||||||
|
'max': 'Max',
|
||||||
|
'other': '$effort',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantEffortSetUp(String effort, String max) {
|
||||||
|
return 'Effort $effort, up to $max';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantEffortFixed(String effort) {
|
||||||
|
return 'Effort $effort';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantNameUnset(String name) {
|
||||||
|
return '$name (no longer set up)';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantNotAgreedCount(int count) {
|
||||||
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
|
count,
|
||||||
|
locale: localeName,
|
||||||
|
other: '$count not agreed to yet',
|
||||||
|
one: '1 not agreed to yet',
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGrant => 'Grant assistants';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantSave => 'Grant';
|
String get assistantGrantSave => 'Grant';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantChange => 'Change limits';
|
String get assistantGrantChange => 'Change assistants';
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantAssistant => 'Assistant';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantDaily => 'Limit a day';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantMonthly => 'Limit a month';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantLimitHelper =>
|
String get assistantGrantLimitHelper =>
|
||||||
'Empty for none. Days and months are UTC\'s.';
|
'Empty for none. Days and months are UTC\'s.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantLimitInvalid => 'Not a number this takes';
|
String get assistantGrantBudget => 'Budget, for all of them together';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantNoHandle => 'No account has this handle.';
|
String get assistantGrantMoneyDaily => 'Money a day';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGrantMoneyMonthly => 'Money a month';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGrantTokensDaily => 'Tokens a day';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGrantTokensMonthly => 'Tokens a month';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGrantMoneyNote =>
|
||||||
|
'Money counts the assistants with prices, tokens count every one. Whichever runs out first stops them.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantGrantReplaces(int count) {
|
||||||
|
String _temp0 = intl.Intl.pluralLogic(
|
||||||
|
count,
|
||||||
|
locale: localeName,
|
||||||
|
other: 'Granting replaces the budgets these people have:',
|
||||||
|
one: 'Granting replaces the budget this person has:',
|
||||||
|
);
|
||||||
|
return '$_temp0';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String assistantGrantUnsetDropped(String names) {
|
||||||
|
return 'Saving takes back what is no longer set up: $names.';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantGrantLimitInvalid => 'Not a number this takes';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantTokens => 'tokens';
|
String get assistantGrantTokens => 'tokens';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantGrantNote =>
|
String get assistantGrantNote =>
|
||||||
'They agree once to what goes where before they ask it. You see how much they use, never what they ask.';
|
'They agree once to what goes where before they ask one. Assistants they have already stay. You see how much they use, never what they ask.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String assistantPerDay(String amount) {
|
String assistantPerDay(String amount) {
|
||||||
|
|
@ -1345,30 +1489,15 @@ class HalkyonAppLocalizationsEn extends HalkyonAppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get assistantNoLimits => 'No limits';
|
String get assistantNoLimits => 'No limits';
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantAgreed => 'Agreed';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantNotAgreed => 'Not agreed yet';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantOutdated =>
|
|
||||||
'Its prices changed: set its limits again';
|
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantGrantUnset => 'No longer set up';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantRevoke => 'Take back';
|
String get assistantRevoke => 'Take back';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String assistantRevokeTitle(String assistant) {
|
String get assistantRevokeAllTitle => 'Take back every assistant?';
|
||||||
return 'Take back $assistant?';
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantRevokeMessage =>
|
String get assistantRevokeMessage =>
|
||||||
'They can no longer ask it. What they used stays counted.';
|
'They can no longer ask any of them, and their budget goes. What they used stays counted.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantUseByMonth => 'Use by month';
|
String get assistantUseByMonth => 'Use by month';
|
||||||
|
|
@ -1418,10 +1547,10 @@ class HalkyonAppLocalizationsEn extends HalkyonAppLocalizations {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantAgree => 'Read and agree';
|
String get assistantYourBudget => 'Your budget, for all of them';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantWaitsForLimits => 'Waits for its limits to be set again';
|
String get assistantAgree => 'Read and agree';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantYourUse => 'Your use';
|
String get assistantYourUse => 'Your use';
|
||||||
|
|
@ -1544,6 +1673,10 @@ class HalkyonAppLocalizationsEn extends HalkyonAppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get assistantProblemNotAgreed => 'Agree to what goes where first.';
|
String get assistantProblemNotAgreed => 'Agree to what goes where first.';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get assistantProblemInvalidEffort =>
|
||||||
|
'It doesn\'t think at this effort.';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String assistantProblemLimitReached(String time) {
|
String assistantProblemLimitReached(String time) {
|
||||||
return 'Its limit is reached until $time.';
|
return 'Its limit is reached until $time.';
|
||||||
|
|
@ -1552,10 +1685,6 @@ class HalkyonAppLocalizationsEn extends HalkyonAppLocalizations {
|
||||||
@override
|
@override
|
||||||
String get assistantProblemLimitReachedNow => 'Its limit is reached for now.';
|
String get assistantProblemLimitReachedNow => 'Its limit is reached for now.';
|
||||||
|
|
||||||
@override
|
|
||||||
String get assistantProblemLimitOutdated =>
|
|
||||||
'It waits for its limits to be set again.';
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get assistantProblemImagesNotRead => 'It reads no images.';
|
String get assistantProblemImagesNotRead => 'It reads no images.';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,199 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:halkyon_app/src/assistant/data/assistant_repository.dart';
|
||||||
|
import 'package:halkyon_app/src/assistant/model/assistant_grant.dart';
|
||||||
|
import 'package:halkyon_app/src/assistant/model/assistant_offer.dart';
|
||||||
|
import 'package:halkyon_app/src/operator/model/operated_account.dart';
|
||||||
|
|
||||||
|
/// What the sheet granting assistants, or changing a person's, shows.
|
||||||
|
final class const AssistantGrantState({
|
||||||
|
/// The people to grant to, in the order added; none while changing.
|
||||||
|
final List<OperatedAccount> accounts = const [],
|
||||||
|
|
||||||
|
/// The person whose assistants and budget are changed; null while
|
||||||
|
/// granting.
|
||||||
|
final AssistantGrantee? grantee,
|
||||||
|
|
||||||
|
/// The IDs of the assistants chosen.
|
||||||
|
final Set<String> chosen = const {},
|
||||||
|
|
||||||
|
/// Those of [accounts] with an assistant or a budget already, whose
|
||||||
|
/// budget a grant replaces.
|
||||||
|
final List<AssistantGrantee> replaced = const [],
|
||||||
|
|
||||||
|
/// Saving.
|
||||||
|
final bool busy = false,
|
||||||
|
|
||||||
|
/// Why the instance refused the last save; null while it didn't.
|
||||||
|
final OperatorFailed? refused,
|
||||||
|
}) with Equatable {
|
||||||
|
/// Whether it can be saved: someone to grant to, and an assistant.
|
||||||
|
bool get canSave =>
|
||||||
|
!busy && chosen.isNotEmpty && (grantee != null || accounts.isNotEmpty);
|
||||||
|
|
||||||
|
AssistantGrantState copyWith({
|
||||||
|
List<OperatedAccount>? accounts,
|
||||||
|
Set<String>? chosen,
|
||||||
|
List<AssistantGrantee>? replaced,
|
||||||
|
bool? busy,
|
||||||
|
OperatorFailed? refused,
|
||||||
|
bool clearRefused = false,
|
||||||
|
}) => AssistantGrantState(
|
||||||
|
accounts: accounts ?? this.accounts,
|
||||||
|
grantee: grantee,
|
||||||
|
chosen: chosen ?? this.chosen,
|
||||||
|
replaced: replaced ?? this.replaced,
|
||||||
|
busy: busy ?? this.busy,
|
||||||
|
refused: clearRefused ? null : refused ?? this.refused,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [
|
||||||
|
accounts,
|
||||||
|
grantee,
|
||||||
|
chosen,
|
||||||
|
replaced,
|
||||||
|
busy,
|
||||||
|
refused,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Granting assistants to several people at once, all with one budget, or
|
||||||
|
/// changing one person's (Halkyon Learn's ADR 0021, D58): the people by
|
||||||
|
/// handle, only accounts there are (D61), the assistants chosen one by one
|
||||||
|
/// or a provider's at once; those whose budget a grant replaces named
|
||||||
|
/// before it does.
|
||||||
|
class AssistantGrantCubit(
|
||||||
|
final AssistantOperatorRepository _operators, {
|
||||||
|
|
||||||
|
/// The assistants set, whose order the chosen go in.
|
||||||
|
required final AssistantsSetUp setUp,
|
||||||
|
|
||||||
|
/// The people to grant to at first.
|
||||||
|
List<OperatedAccount> accounts = const [],
|
||||||
|
|
||||||
|
/// The person to change instead; their assistants still set are chosen
|
||||||
|
/// at first.
|
||||||
|
AssistantGrantee? grantee,
|
||||||
|
}) extends Cubit<AssistantGrantState> {
|
||||||
|
this
|
||||||
|
: super(
|
||||||
|
AssistantGrantState(
|
||||||
|
accounts: accounts,
|
||||||
|
grantee: grantee,
|
||||||
|
chosen: {
|
||||||
|
for (final grant in grantee?.grants ?? const <AssistantGrant>[])
|
||||||
|
if (grant.assistantSet && setUp.named(grant.assistant) != null)
|
||||||
|
grant.assistant,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
unawaited(_readReplaced());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Which read answered last, so an older answer arriving late is
|
||||||
|
/// dropped.
|
||||||
|
var _reads = 0;
|
||||||
|
|
||||||
|
/// Adds [account] to those granted to, once.
|
||||||
|
void add(OperatedAccount account) {
|
||||||
|
if (state.accounts.any((each) => each.id == account.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit(state.copyWith(accounts: [...state.accounts, account]));
|
||||||
|
unawaited(_readReplaced());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes the account [id] names off those granted to.
|
||||||
|
void remove(String id) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
accounts: [
|
||||||
|
for (final account in state.accounts)
|
||||||
|
if (account.id != id) account,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
unawaited(_readReplaced());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Chooses the assistant [id] names, or leaves it out.
|
||||||
|
void choose(String id, {required bool on}) => chooseAll([id], on: on);
|
||||||
|
|
||||||
|
/// Chooses each of [ids] (a provider's), or leaves them out.
|
||||||
|
void chooseAll(Iterable<String> ids, {required bool on}) => emit(
|
||||||
|
state.copyWith(
|
||||||
|
chosen: on
|
||||||
|
? {...state.chosen, ...ids}
|
||||||
|
: state.chosen.difference(ids.toSet()),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Grants each person each assistant chosen, or gives the person changed
|
||||||
|
/// exactly those, with [budget]; whether it was saved, the instance's
|
||||||
|
/// refusal the state's otherwise.
|
||||||
|
Future<bool> save(AssistantBudget budget) async {
|
||||||
|
if (!state.canSave) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
emit(state.copyWith(busy: true, clearRefused: true));
|
||||||
|
final assistants = [
|
||||||
|
for (final assistant in setUp.assistants)
|
||||||
|
if (state.chosen.contains(assistant.id)) assistant.id,
|
||||||
|
];
|
||||||
|
try {
|
||||||
|
if (state.grantee case final grantee?) {
|
||||||
|
await _operators.change(
|
||||||
|
account: grantee.account,
|
||||||
|
assistants: assistants,
|
||||||
|
budget: budget,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await _operators.grant(
|
||||||
|
accounts: [for (final account in state.accounts) account.id],
|
||||||
|
assistants: assistants,
|
||||||
|
budget: budget,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} on Object catch (error) {
|
||||||
|
log('Granting assistants failed: ${error.runtimeType}', name: 'AI');
|
||||||
|
if (!isClosed) {
|
||||||
|
emit(
|
||||||
|
state.copyWith(
|
||||||
|
busy: false,
|
||||||
|
refused: error is OperatorFailed
|
||||||
|
? error
|
||||||
|
: const OperatorFailed(OperatorProblem.failed),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isClosed) {
|
||||||
|
emit(state.copyWith(busy: false));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _readReplaced() async {
|
||||||
|
final read = ++_reads;
|
||||||
|
List<AssistantGrantee> replaced;
|
||||||
|
try {
|
||||||
|
replaced = [
|
||||||
|
for (final grantee in await _operators.granteesOf([
|
||||||
|
for (final account in state.accounts) account.id,
|
||||||
|
]))
|
||||||
|
if (grantee.grants.isNotEmpty || !grantee.budget.isNone) grantee,
|
||||||
|
];
|
||||||
|
} on Object catch (error) {
|
||||||
|
log('Reading budgets failed: ${error.runtimeType}', name: 'AI');
|
||||||
|
replaced = const [];
|
||||||
|
}
|
||||||
|
if (!isClosed && read == _reads) {
|
||||||
|
emit(state.copyWith(replaced: replaced));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,170 +0,0 @@
|
||||||
import 'dart:developer';
|
|
||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:halkyon_app/src/assistant/data/assistant_repository.dart';
|
|
||||||
import 'package:halkyon_app/src/assistant/model/assistant_grant.dart';
|
|
||||||
import 'package:halkyon_app/src/operator/model/operated_account.dart';
|
|
||||||
|
|
||||||
/// What the operator's screen shows of the assistants.
|
|
||||||
final class const AssistantGrantsState({
|
|
||||||
/// Reading the assistants set and the grants.
|
|
||||||
final bool loading = true,
|
|
||||||
|
|
||||||
/// Why they couldn't be read; null while they could.
|
|
||||||
final OperatorFailed? unreadable,
|
|
||||||
|
|
||||||
/// The assistants set in `deploy/assistants.env`, without their keys.
|
|
||||||
final AssistantsSetUp setUp = const AssistantsSetUp(),
|
|
||||||
|
|
||||||
/// Every grant, by handle.
|
|
||||||
final List<AssistantGrant> grants = const [],
|
|
||||||
|
|
||||||
/// What is under way: a grant's key ([keyOf]), or `grant` for a new one;
|
|
||||||
/// null for nothing.
|
|
||||||
final String? busy,
|
|
||||||
|
|
||||||
/// Why the last action failed, nothing changed; null for none.
|
|
||||||
final OperatorFailed? problem,
|
|
||||||
}) with Equatable {
|
|
||||||
/// Whether there is anything to show: an assistant set, or a grant left
|
|
||||||
/// of one no longer set.
|
|
||||||
bool get showsAnything => setUp.assistants.isNotEmpty || grants.isNotEmpty;
|
|
||||||
|
|
||||||
/// What names [grant] while it is acted on.
|
|
||||||
static String keyOf(AssistantGrant grant) =>
|
|
||||||
'${grant.account}/${grant.assistant}';
|
|
||||||
|
|
||||||
AssistantGrantsState copyWith({
|
|
||||||
bool? loading,
|
|
||||||
OperatorFailed? unreadable,
|
|
||||||
bool readable = false,
|
|
||||||
AssistantsSetUp? setUp,
|
|
||||||
List<AssistantGrant>? grants,
|
|
||||||
String? busy,
|
|
||||||
bool clearBusy = false,
|
|
||||||
OperatorFailed? problem,
|
|
||||||
bool clearProblem = false,
|
|
||||||
}) => AssistantGrantsState(
|
|
||||||
loading: loading ?? this.loading,
|
|
||||||
unreadable: readable ? null : unreadable ?? this.unreadable,
|
|
||||||
setUp: setUp ?? this.setUp,
|
|
||||||
grants: grants ?? this.grants,
|
|
||||||
busy: clearBusy ? null : busy ?? this.busy,
|
|
||||||
problem: clearProblem ? null : problem ?? this.problem,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [
|
|
||||||
loading,
|
|
||||||
unreadable,
|
|
||||||
setUp,
|
|
||||||
grants,
|
|
||||||
busy,
|
|
||||||
problem,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The assistants the operator grants (Halkyon Learn's ADR 0021, points 2,
|
|
||||||
/// 6 and 8): granted by handle with their limits, the limits changed, taken
|
|
||||||
/// back. One action at a time, the grants read anew after each.
|
|
||||||
class AssistantGrantsCubit(final AssistantOperatorRepository _operators)
|
|
||||||
extends Cubit<AssistantGrantsState> {
|
|
||||||
this : super(const AssistantGrantsState());
|
|
||||||
|
|
||||||
/// Reads the assistants set and the grants.
|
|
||||||
Future<void> load() async {
|
|
||||||
try {
|
|
||||||
final setUp = await _operators.setUp();
|
|
||||||
final grants = await _operators.grants();
|
|
||||||
if (!isClosed) {
|
|
||||||
emit(
|
|
||||||
state.copyWith(
|
|
||||||
loading: false,
|
|
||||||
readable: true,
|
|
||||||
setUp: setUp,
|
|
||||||
grants: grants,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} on Object catch (error) {
|
|
||||||
log('Reading the grants failed: ${error.runtimeType}', name: 'AI');
|
|
||||||
if (!isClosed) {
|
|
||||||
emit(state.copyWith(loading: false, unreadable: _failure(error)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Grants [assistant] to the account with [handle], with [dailyLimit] and
|
|
||||||
/// [monthlyLimit] in its pricing's unit; why it failed, or null once
|
|
||||||
/// granted. The sheet asking says it, so it stays out of `problem`.
|
|
||||||
Future<OperatorFailed?> grant({
|
|
||||||
required String handle,
|
|
||||||
required String assistant,
|
|
||||||
required int? dailyLimit,
|
|
||||||
required int? monthlyLimit,
|
|
||||||
}) => _act(
|
|
||||||
'grant',
|
|
||||||
() => _operators.grant(
|
|
||||||
handle: handle,
|
|
||||||
assistant: assistant,
|
|
||||||
dailyLimit: dailyLimit,
|
|
||||||
monthlyLimit: monthlyLimit,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Sets [grant]'s limits; why it failed, or null once set.
|
|
||||||
Future<OperatorFailed?> changeLimits(
|
|
||||||
AssistantGrant grant, {
|
|
||||||
required int? dailyLimit,
|
|
||||||
required int? monthlyLimit,
|
|
||||||
}) => _act(
|
|
||||||
AssistantGrantsState.keyOf(grant),
|
|
||||||
() => _operators.changeLimits(
|
|
||||||
account: grant.account,
|
|
||||||
assistant: grant.assistant,
|
|
||||||
dailyLimit: dailyLimit,
|
|
||||||
monthlyLimit: monthlyLimit,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Takes [grant] back; a failure is the state's `problem`.
|
|
||||||
Future<void> revoke(AssistantGrant grant) async {
|
|
||||||
final failed = await _act(
|
|
||||||
AssistantGrantsState.keyOf(grant),
|
|
||||||
() => _operators.revoke(
|
|
||||||
account: grant.account,
|
|
||||||
assistant: grant.assistant,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
if (failed != null && !isClosed) {
|
|
||||||
emit(state.copyWith(problem: failed));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<OperatorFailed?> _act(
|
|
||||||
String what,
|
|
||||||
Future<void> Function() action,
|
|
||||||
) async {
|
|
||||||
if (state.busy != null) {
|
|
||||||
return const OperatorFailed(OperatorProblem.failed);
|
|
||||||
}
|
|
||||||
emit(state.copyWith(busy: what, clearProblem: true));
|
|
||||||
OperatorFailed? failed;
|
|
||||||
try {
|
|
||||||
await action();
|
|
||||||
} on Object catch (error) {
|
|
||||||
log('An assistant grant failed: ${error.runtimeType}', name: 'AI');
|
|
||||||
failed = _failure(error);
|
|
||||||
}
|
|
||||||
await load();
|
|
||||||
if (!isClosed) {
|
|
||||||
emit(state.copyWith(clearBusy: true));
|
|
||||||
}
|
|
||||||
return failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
static OperatorFailed _failure(Object error) => error is OperatorFailed
|
|
||||||
? error
|
|
||||||
: const OperatorFailed(OperatorProblem.failed);
|
|
||||||
}
|
|
||||||
|
|
@ -10,8 +10,9 @@ final class const AssistantUseState({
|
||||||
/// The month shown, its first day in UTC.
|
/// The month shown, its first day in UTC.
|
||||||
required final DateTime month,
|
required final DateTime month,
|
||||||
|
|
||||||
/// The months the instance keeps, the latest first: this one and the
|
/// The months the instance keeps, the oldest first, as a timeline runs:
|
||||||
/// two before (Halkyon Learn's ADR 0021, D41).
|
/// the two before this one, and this one (Halkyon Learn's ADR 0021, D41,
|
||||||
|
/// D57).
|
||||||
required final List<DateTime> months,
|
required final List<DateTime> months,
|
||||||
|
|
||||||
/// Reading it.
|
/// Reading it.
|
||||||
|
|
@ -39,7 +40,7 @@ class AssistantUseCubit(
|
||||||
return AssistantUseState(
|
return AssistantUseState(
|
||||||
month: month,
|
month: month,
|
||||||
months: [
|
months: [
|
||||||
for (var back = 0; back < 3; back++)
|
for (var back = 2; back >= 0; back--)
|
||||||
DateTime.utc(month.year, month.month - back),
|
DateTime.utc(month.year, month.month - back),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import 'package:halkyon_llm/halkyon_llm.dart';
|
||||||
/// Learn's ADR 0021, points 6 to 8), on the signed-in person's session:
|
/// Learn's ADR 0021, points 6 to 8), on the signed-in person's session:
|
||||||
/// failures are `AssistantFailed`s. Tests replace it with a fake.
|
/// failures are `AssistantFailed`s. Tests replace it with a fake.
|
||||||
abstract interface class AssistantGateway() {
|
abstract interface class AssistantGateway() {
|
||||||
/// The assistants granted to the person, with what is left of their
|
/// The assistants granted to the person, with their budget and what is
|
||||||
/// limits; none on a member of another's register (D43).
|
/// used of it; none on a member of another's register (D43).
|
||||||
Future<AssistantsOffer> offered();
|
Future<AssistantsOffer> offered();
|
||||||
|
|
||||||
/// Records that the person agrees to what goes where with [assistant],
|
/// Records that the person agrees to what goes where with [assistant],
|
||||||
|
|
@ -25,26 +25,39 @@ abstract interface class AssistantGateway() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The boundary to granting assistants (Halkyon Learn's ADR 0021, points 2,
|
/// The boundary to granting assistants (Halkyon Learn's ADR 0021, points 2,
|
||||||
/// 6 and 8), on the operator's session: refusals are `OperatorFailed`s.
|
/// 6 and 8, D58), on the operator's session: refusals are `OperatorFailed`s.
|
||||||
abstract interface class AssistantOperatorGateway() {
|
abstract interface class AssistantOperatorGateway() {
|
||||||
/// The assistants set in `deploy/assistants.env`, without their keys.
|
/// The assistants set in `deploy/assistants.env`, without their keys, and
|
||||||
|
/// how many people have any.
|
||||||
Future<AssistantsSetUp> setUp();
|
Future<AssistantsSetUp> setUp();
|
||||||
|
|
||||||
/// Every grant, by handle.
|
/// The people with an assistant whose handle starts with [startingWith]
|
||||||
Future<List<AssistantGrant>> grants();
|
/// (every one for nothing), by handle, a page from [offset] on.
|
||||||
|
Future<List<AssistantGrantee>> grantees({
|
||||||
/// Grants [assistant] to [account] with [dailyLimit] and [monthlyLimit]
|
required String startingWith,
|
||||||
/// (null: none) in its pricing's unit; granted already, the limits change
|
required int offset,
|
||||||
/// and the agreement stays.
|
|
||||||
Future<void> grant({
|
|
||||||
required String account,
|
|
||||||
required String assistant,
|
|
||||||
required int? dailyLimit,
|
|
||||||
required int? monthlyLimit,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Takes [assistant] back from [account].
|
/// Those of [accounts] with an assistant or a budget.
|
||||||
Future<void> revoke({required String account, required String assistant});
|
Future<List<AssistantGrantee>> granteesOf(List<String> accounts);
|
||||||
|
|
||||||
|
/// Grants each of [accounts] each of [assistants] they lack, their
|
||||||
|
/// agreements to the others kept, and sets each one's budget to [budget].
|
||||||
|
Future<void> grant({
|
||||||
|
required List<String> accounts,
|
||||||
|
required List<String> assistants,
|
||||||
|
required AssistantBudget budget,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Gives [account] exactly [assistants] and [budget].
|
||||||
|
Future<void> change({
|
||||||
|
required String account,
|
||||||
|
required List<String> assistants,
|
||||||
|
required AssistantBudget budget,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Takes every assistant and the budget back from [account].
|
||||||
|
Future<void> revoke(String account);
|
||||||
|
|
||||||
/// Everyone's use in the UTC month [month] is in, per person and
|
/// Everyone's use in the UTC month [month] is in, per person and
|
||||||
/// assistant.
|
/// assistant.
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,10 @@ protocol.AssistantQuestion questionOf(String assistant, LlmRequest request) =>
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
maxOutputTokens: request.maxOutputTokens,
|
maxOutputTokens: request.maxOutputTokens,
|
||||||
|
effort: switch (request.effort) {
|
||||||
|
final effort? => protocol.AssistantEffort.fromJson(effort.key),
|
||||||
|
null => null,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
/// [message] as the module's endpoint carries it.
|
/// [message] as the module's endpoint carries it.
|
||||||
|
|
@ -223,8 +227,8 @@ AssistantFailed failureOf(Object error) => switch (error) {
|
||||||
protocol.AssistantRefusal.notGranted => AssistantProblem.notGranted,
|
protocol.AssistantRefusal.notGranted => AssistantProblem.notGranted,
|
||||||
protocol.AssistantRefusal.notAgreed => AssistantProblem.notAgreed,
|
protocol.AssistantRefusal.notAgreed => AssistantProblem.notAgreed,
|
||||||
protocol.AssistantRefusal.limitReached => AssistantProblem.limitReached,
|
protocol.AssistantRefusal.limitReached => AssistantProblem.limitReached,
|
||||||
protocol.AssistantRefusal.limitOutdated =>
|
protocol.AssistantRefusal.invalidEffort =>
|
||||||
AssistantProblem.limitOutdated,
|
AssistantProblem.invalidEffort,
|
||||||
protocol.AssistantRefusal.imagesNotRead =>
|
protocol.AssistantRefusal.imagesNotRead =>
|
||||||
AssistantProblem.imagesNotRead,
|
AssistantProblem.imagesNotRead,
|
||||||
protocol.AssistantRefusal.malformed => AssistantProblem.malformed,
|
protocol.AssistantRefusal.malformed => AssistantProblem.malformed,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import 'package:halkyon_app/src/assistant/model/assistant_grant.dart';
|
||||||
import 'package:halkyon_app/src/assistant/model/assistant_offer.dart';
|
import 'package:halkyon_app/src/assistant/model/assistant_offer.dart';
|
||||||
import 'package:halkyon_app/src/assistant/model/own_assistant.dart';
|
import 'package:halkyon_app/src/assistant/model/own_assistant.dart';
|
||||||
import 'package:halkyon_app/src/operator/data/instance_operator_gateway.dart';
|
import 'package:halkyon_app/src/operator/data/instance_operator_gateway.dart';
|
||||||
|
import 'package:halkyon_app/src/operator/data/instance_operator_repository.dart';
|
||||||
import 'package:halkyon_app/src/operator/model/operated_account.dart';
|
import 'package:halkyon_app/src/operator/model/operated_account.dart';
|
||||||
import 'package:halkyon_llm/halkyon_llm.dart';
|
import 'package:halkyon_llm/halkyon_llm.dart';
|
||||||
|
|
||||||
|
|
@ -22,6 +23,18 @@ sealed class const AssistantChoice() {
|
||||||
|
|
||||||
/// Whether it reads images.
|
/// Whether it reads images.
|
||||||
bool get images;
|
bool get images;
|
||||||
|
|
||||||
|
/// The efforts a question may ask it at, from the least (D63); empty:
|
||||||
|
/// none to choose.
|
||||||
|
List<LlmEffort> get efforts;
|
||||||
|
|
||||||
|
/// The effort a question takes unless it names one; null: the model's
|
||||||
|
/// own.
|
||||||
|
LlmEffort? get effort;
|
||||||
|
|
||||||
|
/// The ceiling on output tokens a question at [asked] asks for, its
|
||||||
|
/// thinking within it.
|
||||||
|
int outputCeilingAt(LlmEffort? asked);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An assistant the operator granted, asked through the instance.
|
/// An assistant the operator granted, asked through the instance.
|
||||||
|
|
@ -35,12 +48,27 @@ final class const GrantedAssistant(final OfferedAssistant assistant)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get images => assistant.images;
|
bool get images => assistant.images;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<LlmEffort> get efforts => assistant.efforts;
|
||||||
|
|
||||||
|
@override
|
||||||
|
LlmEffort? get effort => assistant.effort;
|
||||||
|
|
||||||
|
/// The effort's, as the instance bounds it.
|
||||||
|
@override
|
||||||
|
int outputCeilingAt(LlmEffort? asked) =>
|
||||||
|
(asked ?? effort)?.outputCeiling ?? LlmEffort.defaultOutputCeiling;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An assistant with the person's own key, asked directly: the instance
|
/// An assistant with the person's own key, asked directly: the instance
|
||||||
/// sees neither the key nor what is asked, and counts nothing.
|
/// sees neither the key nor what is asked, and counts nothing.
|
||||||
final class const PersonalAssistant(final OwnAssistant assistant)
|
final class const PersonalAssistant(final OwnAssistant assistant)
|
||||||
extends AssistantChoice {
|
extends AssistantChoice {
|
||||||
|
/// The ceiling every question to one asks for: a long explanation, which
|
||||||
|
/// any model takes, as nobody knows how far the person's model goes.
|
||||||
|
static const outputCeiling = 4096;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get name => assistant.name;
|
String get name => assistant.name;
|
||||||
|
|
||||||
|
|
@ -49,6 +77,15 @@ final class const PersonalAssistant(final OwnAssistant assistant)
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get images => assistant.images;
|
bool get images => assistant.images;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<LlmEffort> get efforts => const [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
LlmEffort? get effort => null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int outputCeilingAt(LlmEffort? asked) => outputCeiling;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The assistants the signed-in person may ask (Halkyon Learn's ADR 0021):
|
/// The assistants the signed-in person may ask (Halkyon Learn's ADR 0021):
|
||||||
|
|
@ -67,8 +104,8 @@ class AssistantRepository(
|
||||||
/// assistants to read them anew.
|
/// assistants to read them anew.
|
||||||
Stream<void> get changes => _changes.stream;
|
Stream<void> get changes => _changes.stream;
|
||||||
|
|
||||||
/// The assistants granted to the person, with what is left of their
|
/// The assistants granted to the person, with their budget and what is
|
||||||
/// limits.
|
/// used of it.
|
||||||
Future<AssistantsOffer> offered() => _gateway.offered();
|
Future<AssistantsOffer> offered() => _gateway.offered();
|
||||||
|
|
||||||
/// Records that the person agrees to what goes where with [assistant].
|
/// Records that the person agrees to what goes where with [assistant].
|
||||||
|
|
@ -109,6 +146,7 @@ class AssistantRepository(
|
||||||
return _llm
|
return _llm
|
||||||
.send(
|
.send(
|
||||||
endpoint,
|
endpoint,
|
||||||
|
// No effort: the person's own key gets none to choose from.
|
||||||
LlmRequest(
|
LlmRequest(
|
||||||
messages: request.messages,
|
messages: request.messages,
|
||||||
maxOutputTokens: request.maxOutputTokens,
|
maxOutputTokens: request.maxOutputTokens,
|
||||||
|
|
@ -133,12 +171,12 @@ class AssistantRepository(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Granting assistants (Halkyon Learn's ADR 0021, points 2, 6 and 8), as
|
/// Granting assistants (Halkyon Learn's ADR 0021, points 2, 6 and 8, D58),
|
||||||
/// the operator's screen uses it. Refusals are [OperatorFailed]s.
|
/// as the operator's screens use it. Refusals are [OperatorFailed]s.
|
||||||
class AssistantOperatorRepository(
|
class AssistantOperatorRepository(
|
||||||
final AssistantOperatorGateway _gateway,
|
final AssistantOperatorGateway _gateway,
|
||||||
|
|
||||||
/// Where accounts are found by their handles.
|
/// Where the accounts to grant to are found by their handles.
|
||||||
final InstanceOperatorGateway _accounts,
|
final InstanceOperatorGateway _accounts,
|
||||||
) {
|
) {
|
||||||
final _changes = StreamController<void>.broadcast();
|
final _changes = StreamController<void>.broadcast();
|
||||||
|
|
@ -146,61 +184,56 @@ class AssistantOperatorRepository(
|
||||||
/// Every change made through this repository.
|
/// Every change made through this repository.
|
||||||
Stream<void> get changes => _changes.stream;
|
Stream<void> get changes => _changes.stream;
|
||||||
|
|
||||||
/// The assistants set, without their keys.
|
/// The assistants set, without their keys, and how many people have any.
|
||||||
Future<AssistantsSetUp> setUp() => _gateway.setUp();
|
Future<AssistantsSetUp> setUp() => _gateway.setUp();
|
||||||
|
|
||||||
/// Every grant, by handle.
|
/// The people with an assistant whose handle starts with [start] (an `@`
|
||||||
Future<List<AssistantGrant>> grants() => _gateway.grants();
|
/// before it left out; every one for nothing typed), by handle, a page
|
||||||
|
/// from [offset] on.
|
||||||
|
Future<List<AssistantGrantee>> grantees(String start, {int offset = 0}) =>
|
||||||
|
_gateway.grantees(
|
||||||
|
startingWith: start.trim().replaceFirst(RegExp('^@'), ''),
|
||||||
|
offset: offset,
|
||||||
|
);
|
||||||
|
|
||||||
/// Grants [assistant] to the account with [handle] (an `@` before it
|
/// The accounts a handle typed as [start] suggests to grant to, at most
|
||||||
/// left out, whatever its case), with [dailyLimit] and [monthlyLimit] in
|
/// [count]: approved and not suspended, whose handle starts so; none for
|
||||||
/// its pricing's unit (null: none); refused as `notFound` for a handle
|
/// nothing typed (D61).
|
||||||
/// nobody has.
|
Future<List<OperatedAccount>> grantable(String start, {int count = 5}) =>
|
||||||
|
suggestedAccounts(
|
||||||
|
_accounts,
|
||||||
|
start,
|
||||||
|
eligible: (account) => !account.waits,
|
||||||
|
count: count,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Those of [accounts] with an assistant or a budget: whose budget a
|
||||||
|
/// grant to them replaces.
|
||||||
|
Future<List<AssistantGrantee>> granteesOf(List<String> accounts) =>
|
||||||
|
accounts.isEmpty ? Future.value(const []) : _gateway.granteesOf(accounts);
|
||||||
|
|
||||||
|
/// Grants each of [accounts] each of [assistants] they lack, the
|
||||||
|
/// agreements to the others kept, and sets each one's budget to [budget].
|
||||||
Future<void> grant({
|
Future<void> grant({
|
||||||
required String handle,
|
required List<String> accounts,
|
||||||
required String assistant,
|
required List<String> assistants,
|
||||||
required int? dailyLimit,
|
required AssistantBudget budget,
|
||||||
required int? monthlyLimit,
|
|
||||||
}) async {
|
|
||||||
final typed = handle.trim().replaceFirst(RegExp('^@'), '');
|
|
||||||
final account = typed.isEmpty
|
|
||||||
? null
|
|
||||||
: (await _accounts.accountsStartingWith(typed))
|
|
||||||
.where(
|
|
||||||
(account) =>
|
|
||||||
account.handle?.toLowerCase() == typed.toLowerCase(),
|
|
||||||
)
|
|
||||||
.firstOrNull;
|
|
||||||
if (account == null) {
|
|
||||||
throw const OperatorFailed(OperatorProblem.notFound);
|
|
||||||
}
|
|
||||||
await changeLimits(
|
|
||||||
account: account.id,
|
|
||||||
assistant: assistant,
|
|
||||||
dailyLimit: dailyLimit,
|
|
||||||
monthlyLimit: monthlyLimit,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the limits of [assistant]'s grant to [account], granting it where
|
|
||||||
/// it isn't; the person's agreement stays.
|
|
||||||
Future<void> changeLimits({
|
|
||||||
required String account,
|
|
||||||
required String assistant,
|
|
||||||
required int? dailyLimit,
|
|
||||||
required int? monthlyLimit,
|
|
||||||
}) => _changing(
|
}) => _changing(
|
||||||
_gateway.grant(
|
_gateway.grant(accounts: accounts, assistants: assistants, budget: budget),
|
||||||
account: account,
|
|
||||||
assistant: assistant,
|
|
||||||
dailyLimit: dailyLimit,
|
|
||||||
monthlyLimit: monthlyLimit,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Takes [assistant] back from [account].
|
/// Gives [account] exactly [assistants] and [budget]; the agreements to
|
||||||
Future<void> revoke({required String account, required String assistant}) =>
|
/// those kept stay.
|
||||||
_changing(_gateway.revoke(account: account, assistant: assistant));
|
Future<void> change({
|
||||||
|
required String account,
|
||||||
|
required List<String> assistants,
|
||||||
|
required AssistantBudget budget,
|
||||||
|
}) => _changing(
|
||||||
|
_gateway.change(account: account, assistants: assistants, budget: budget),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Takes every assistant and the budget back from [account].
|
||||||
|
Future<void> revoke(String account) => _changing(_gateway.revoke(account));
|
||||||
|
|
||||||
/// Everyone's use in the UTC month [month] is in.
|
/// Everyone's use in the UTC month [month] is in.
|
||||||
Future<List<AssistantUse>> figures(DateTime month) =>
|
Future<List<AssistantUse>> figures(DateTime month) =>
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,17 @@ class ServerpodAssistantGateway(final protocol.Caller _base)
|
||||||
model: offer.model,
|
model: offer.model,
|
||||||
images: offer.images,
|
images: offer.images,
|
||||||
agreedAt: offer.agreedAt,
|
agreedAt: offer.agreedAt,
|
||||||
unit: unitOf(offer.limitUnit),
|
effort: effortOf(offer.effort),
|
||||||
dailyLimit: offer.dailyLimit,
|
efforts: [
|
||||||
monthlyLimit: offer.monthlyLimit,
|
for (final effort in offer.efforts) ?effortOf(effort),
|
||||||
usedToday: offer.usedToday,
|
],
|
||||||
usedThisMonth: offer.usedThisMonth,
|
|
||||||
outdated: offer.outdated,
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
ownKeys: offered.ownKeys,
|
ownKeys: offered.ownKeys,
|
||||||
currency: offered.currency,
|
currency: offered.currency,
|
||||||
|
budget: budgetOf(offered.budget),
|
||||||
|
usedToday: budgetUseOf(offered.usedToday),
|
||||||
|
usedThisMonth: budgetUseOf(offered.usedThisMonth),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -73,55 +74,69 @@ class ServerpodAssistantOperatorGateway(final protocol.Caller _base)
|
||||||
images: info.images,
|
images: info.images,
|
||||||
priceInput: info.priceInput,
|
priceInput: info.priceInput,
|
||||||
priceOutput: info.priceOutput,
|
priceOutput: info.priceOutput,
|
||||||
|
effort: effortOf(info.effort),
|
||||||
|
maxEffort: effortOf(info.maxEffort),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
currency: setUp.currency,
|
currency: setUp.currency,
|
||||||
ownKeys: setUp.ownKeys,
|
ownKeys: setUp.ownKeys,
|
||||||
|
people: setUp.people,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<AssistantGrant>> grants() => operatorCall(
|
Future<List<AssistantGrantee>> grantees({
|
||||||
|
required String startingWith,
|
||||||
|
required int offset,
|
||||||
|
}) => operatorCall(
|
||||||
() async => [
|
() async => [
|
||||||
for (final grant in await _base.assistantOperator.grants())
|
...(await _base.assistantOperator.grantees(
|
||||||
AssistantGrant(
|
startingWith: startingWith,
|
||||||
account: grant.account.uuid,
|
offset: offset,
|
||||||
handle: grant.handle,
|
)).map(_granteeOf),
|
||||||
assistant: grant.assistant,
|
|
||||||
assistantSet: grant.assistantSet,
|
|
||||||
grantedAt: grant.grantedAt,
|
|
||||||
unit: unitOf(grant.limitUnit),
|
|
||||||
dailyLimit: grant.dailyLimit,
|
|
||||||
monthlyLimit: grant.monthlyLimit,
|
|
||||||
agreedAt: grant.agreedAt,
|
|
||||||
outdated: grant.outdated,
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<AssistantGrantee>> granteesOf(List<String> accounts) =>
|
||||||
|
operatorCall(
|
||||||
|
() async => [
|
||||||
|
...(await _base.assistantOperator.granteesOf(
|
||||||
|
accounts: [for (final account in accounts) _idOf(account)],
|
||||||
|
)).map(_granteeOf),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> grant({
|
Future<void> grant({
|
||||||
required String account,
|
required List<String> accounts,
|
||||||
required String assistant,
|
required List<String> assistants,
|
||||||
required int? dailyLimit,
|
required AssistantBudget budget,
|
||||||
required int? monthlyLimit,
|
|
||||||
}) => operatorCall(
|
}) => operatorCall(
|
||||||
() => _base.assistantOperator.grant(
|
() => _base.assistantOperator.grant(
|
||||||
account: UuidValue.withValidation(account),
|
accounts: [for (final account in accounts) _idOf(account)],
|
||||||
assistant: assistant,
|
assistants: assistants,
|
||||||
dailyLimit: dailyLimit,
|
budget: _limitsOf(budget),
|
||||||
monthlyLimit: monthlyLimit,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> revoke({required String account, required String assistant}) =>
|
Future<void> change({
|
||||||
operatorCall(
|
required String account,
|
||||||
() => _base.assistantOperator.revoke(
|
required List<String> assistants,
|
||||||
account: UuidValue.withValidation(account),
|
required AssistantBudget budget,
|
||||||
assistant: assistant,
|
}) => operatorCall(
|
||||||
),
|
() => _base.assistantOperator.change(
|
||||||
);
|
account: _idOf(account),
|
||||||
|
assistants: assistants,
|
||||||
|
budget: _limitsOf(budget),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> revoke(String account) => operatorCall(
|
||||||
|
() => _base.assistantOperator.revoke(account: _idOf(account)),
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<AssistantUse>> figures(DateTime month) => operatorCall(
|
Future<List<AssistantUse>> figures(DateTime month) => operatorCall(
|
||||||
|
|
@ -131,13 +146,52 @@ class ServerpodAssistantOperatorGateway(final protocol.Caller _base)
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static UuidValue _idOf(String account) => UuidValue.withValidation(account);
|
||||||
|
|
||||||
|
static protocol.AssistantLimits _limitsOf(AssistantBudget budget) =>
|
||||||
|
protocol.AssistantLimits(
|
||||||
|
dailyCostMicros: budget.dailyCostMicros,
|
||||||
|
monthlyCostMicros: budget.monthlyCostMicros,
|
||||||
|
dailyTokens: budget.dailyTokens,
|
||||||
|
monthlyTokens: budget.monthlyTokens,
|
||||||
|
);
|
||||||
|
|
||||||
|
static AssistantGrantee _granteeOf(protocol.AssistantGrantee grantee) =>
|
||||||
|
AssistantGrantee(
|
||||||
|
account: grantee.account.uuid,
|
||||||
|
handle: grantee.handle,
|
||||||
|
grants: [
|
||||||
|
for (final grant in grantee.assistants)
|
||||||
|
AssistantGrant(
|
||||||
|
assistant: grant.assistant,
|
||||||
|
assistantSet: grant.assistantSet,
|
||||||
|
grantedAt: grant.grantedAt,
|
||||||
|
agreedAt: grant.agreedAt,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
budget: budgetOf(grantee.budget),
|
||||||
|
usedToday: budgetUseOf(grantee.usedToday),
|
||||||
|
usedThisMonth: budgetUseOf(grantee.usedThisMonth),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [unit] in the app's terms.
|
/// [effort] in halkyon_llm's terms; null for none, or one this app doesn't
|
||||||
AssistantLimitUnit unitOf(protocol.AssistantLimitUnit unit) => switch (unit) {
|
/// know.
|
||||||
protocol.AssistantLimitUnit.costMicros => AssistantLimitUnit.money,
|
LlmEffort? effortOf(protocol.AssistantEffort? effort) =>
|
||||||
protocol.AssistantLimitUnit.tokens => AssistantLimitUnit.tokens,
|
effort == null ? null : LlmEffort.named(effort.name);
|
||||||
};
|
|
||||||
|
/// [limits] in the app's terms.
|
||||||
|
AssistantBudget budgetOf(protocol.AssistantLimits limits) => AssistantBudget(
|
||||||
|
dailyCostMicros: limits.dailyCostMicros,
|
||||||
|
monthlyCostMicros: limits.monthlyCostMicros,
|
||||||
|
dailyTokens: limits.dailyTokens,
|
||||||
|
monthlyTokens: limits.monthlyTokens,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// [use] in the app's terms.
|
||||||
|
BudgetUse budgetUseOf(protocol.AssistantUse use) =>
|
||||||
|
BudgetUse(costMicros: use.costMicros, tokens: use.tokens);
|
||||||
|
|
||||||
/// A month's [figures] in the app's terms.
|
/// A month's [figures] in the app's terms.
|
||||||
AssistantUse useOf(protocol.AssistantMonthFigures figures) => AssistantUse(
|
AssistantUse useOf(protocol.AssistantMonthFigures figures) => AssistantUse(
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,12 @@ enum AssistantProblem() {
|
||||||
/// The person hasn't agreed to what goes where, as it is now.
|
/// The person hasn't agreed to what goes where, as it is now.
|
||||||
notAgreed,
|
notAgreed,
|
||||||
|
|
||||||
/// The day's or the month's limit is used up (`AssistantFailed.resetsAt`).
|
/// A day's or a month's limit of the person's budget is used up
|
||||||
|
/// (`AssistantFailed.resetsAt`).
|
||||||
limitReached,
|
limitReached,
|
||||||
|
|
||||||
/// Its limits wait for the operator to set them again.
|
/// The question named an effort the assistant doesn't offer.
|
||||||
limitOutdated,
|
invalidEffort,
|
||||||
|
|
||||||
/// It reads no images.
|
/// It reads no images.
|
||||||
imagesNotRead,
|
imagesNotRead,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:halkyon_app/src/assistant/model/assistant_offer.dart';
|
import 'package:halkyon_app/src/assistant/model/assistant_offer.dart';
|
||||||
|
import 'package:halkyon_llm/halkyon_llm.dart';
|
||||||
|
|
||||||
/// An assistant the operator set in `deploy/assistants.env`, as the
|
/// An assistant the operator set in `deploy/assistants.env`, as the
|
||||||
/// operator's screen shows it (Halkyon Learn's ADR 0021, point 4): never
|
/// operator's screen shows it (Halkyon Learn's ADR 0021, point 4): never
|
||||||
|
|
@ -18,11 +19,17 @@ final class const SetUpAssistant({
|
||||||
|
|
||||||
/// What a million of the answer's tokens cost; null without prices.
|
/// What a million of the answer's tokens cost; null without prices.
|
||||||
final double? priceOutput,
|
final double? priceOutput,
|
||||||
|
|
||||||
|
/// The effort a question takes unless it names one (D63); null: the
|
||||||
|
/// model's own.
|
||||||
|
final LlmEffort? effort,
|
||||||
|
|
||||||
|
/// The highest effort a question may name; null: none to choose.
|
||||||
|
final LlmEffort? maxEffort,
|
||||||
}) with Equatable {
|
}) with Equatable {
|
||||||
/// What its limits are counted in: money with prices, else tokens (D38).
|
/// Whether it has prices, so it counts against a budget's money as well
|
||||||
AssistantLimitUnit get unit => priceInput != null && priceOutput != null
|
/// as its tokens (D59).
|
||||||
? AssistantLimitUnit.money
|
bool get priced => priceInput != null && priceOutput != null;
|
||||||
: AssistantLimitUnit.tokens;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
|
|
@ -34,6 +41,8 @@ final class const SetUpAssistant({
|
||||||
images,
|
images,
|
||||||
priceInput,
|
priceInput,
|
||||||
priceOutput,
|
priceOutput,
|
||||||
|
effort,
|
||||||
|
maxEffort,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,6 +56,9 @@ final class const AssistantsSetUp({
|
||||||
|
|
||||||
/// Whether people may add their own keys.
|
/// Whether people may add their own keys.
|
||||||
final bool ownKeys = false,
|
final bool ownKeys = false,
|
||||||
|
|
||||||
|
/// How many people have an assistant granted.
|
||||||
|
final int people = 0,
|
||||||
}) with Equatable {
|
}) with Equatable {
|
||||||
/// The assistant [id] names; null for one not set (any more).
|
/// The assistant [id] names; null for one not set (any more).
|
||||||
SetUpAssistant? named(String id) {
|
SetUpAssistant? named(String id) {
|
||||||
|
|
@ -59,17 +71,11 @@ final class const AssistantsSetUp({
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [assistants, currency, ownKeys];
|
List<Object?> get props => [assistants, currency, ownKeys, people];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An assistant granted to an account, as the operator sees it (D2, D4).
|
/// An assistant granted to a person, as the operator sees it (D2, D4).
|
||||||
final class const AssistantGrant({
|
final class const AssistantGrant({
|
||||||
/// The account's ID.
|
|
||||||
required final String account,
|
|
||||||
|
|
||||||
/// Its handle; null for one without.
|
|
||||||
final String? handle,
|
|
||||||
|
|
||||||
/// The assistant's ID.
|
/// The assistant's ID.
|
||||||
required final String assistant,
|
required final String assistant,
|
||||||
|
|
||||||
|
|
@ -78,29 +84,45 @@ final class const AssistantGrant({
|
||||||
final bool assistantSet = true,
|
final bool assistantSet = true,
|
||||||
required final DateTime grantedAt,
|
required final DateTime grantedAt,
|
||||||
|
|
||||||
/// What [dailyLimit] and [monthlyLimit] are counted in.
|
|
||||||
final AssistantLimitUnit unit = AssistantLimitUnit.tokens,
|
|
||||||
final int? dailyLimit,
|
|
||||||
final int? monthlyLimit,
|
|
||||||
|
|
||||||
/// When the person agreed to what goes where; null until they do.
|
/// When the person agreed to what goes where; null until they do.
|
||||||
final DateTime? agreedAt,
|
final DateTime? agreedAt,
|
||||||
|
|
||||||
/// Whether its limits are in another unit than the pricing's now, to be
|
|
||||||
/// set again.
|
|
||||||
final bool outdated = false,
|
|
||||||
}) with Equatable {
|
}) with Equatable {
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [assistant, assistantSet, grantedAt, agreedAt];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A person granted assistants, as the operator sees them (D58): which,
|
||||||
|
/// their budget for all of them, and what they used of it.
|
||||||
|
final class const AssistantGrantee({
|
||||||
|
/// The account's ID.
|
||||||
|
required final String account,
|
||||||
|
|
||||||
|
/// Its handle; null for one without.
|
||||||
|
final String? handle,
|
||||||
|
|
||||||
|
/// Their assistants, in the operator's order (those set no more last).
|
||||||
|
final List<AssistantGrant> grants = const [],
|
||||||
|
|
||||||
|
/// Their budget; none: no limits.
|
||||||
|
final AssistantBudget budget = AssistantBudget.none,
|
||||||
|
|
||||||
|
/// What this UTC day used so far.
|
||||||
|
final BudgetUse usedToday = BudgetUse.nothing,
|
||||||
|
|
||||||
|
/// What this UTC month used so far.
|
||||||
|
final BudgetUse usedThisMonth = BudgetUse.nothing,
|
||||||
|
}) with Equatable {
|
||||||
|
/// The budget's limits with what is left of each.
|
||||||
|
List<BudgetLeft> get left =>
|
||||||
|
budgetLeft(budget, today: usedToday, thisMonth: usedThisMonth);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
account,
|
account,
|
||||||
handle,
|
handle,
|
||||||
assistant,
|
grants,
|
||||||
assistantSet,
|
budget,
|
||||||
grantedAt,
|
usedToday,
|
||||||
unit,
|
usedThisMonth,
|
||||||
dailyLimit,
|
|
||||||
monthlyLimit,
|
|
||||||
agreedAt,
|
|
||||||
outdated,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:halkyon_llm/halkyon_llm.dart';
|
||||||
|
|
||||||
/// What an assistant's limits and figures are counted in (Halkyon Learn's
|
/// What an amount of a budget or of use is counted in (Halkyon Learn's ADR
|
||||||
/// ADR 0021, D38): money where the operator set its prices, else tokens.
|
/// 0021, D59): money, or tokens.
|
||||||
enum AssistantLimitUnit() {
|
enum AssistantLimitUnit() {
|
||||||
/// Millionths of the instance's currency (`AssistantsOffer.currency`).
|
/// Millionths of the instance's currency (`AssistantsOffer.currency`).
|
||||||
money,
|
money,
|
||||||
|
|
@ -12,9 +13,92 @@ enum AssistantLimitUnit() {
|
||||||
tokens,
|
tokens,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A person's budget for all their assistants together (Halkyon Learn's
|
||||||
|
/// ADR 0021, D58 and D59): a limit per UTC day and month, in money and in
|
||||||
|
/// tokens, each null for none. Whichever runs out first stops them: the
|
||||||
|
/// money the assistants with prices, the tokens every one.
|
||||||
|
final class const AssistantBudget({
|
||||||
|
/// The most a UTC day may cost, in millionths of the currency.
|
||||||
|
final int? dailyCostMicros,
|
||||||
|
|
||||||
|
/// The most a UTC month may cost, in millionths of the currency.
|
||||||
|
final int? monthlyCostMicros,
|
||||||
|
|
||||||
|
/// The most tokens a UTC day may use.
|
||||||
|
final int? dailyTokens,
|
||||||
|
|
||||||
|
/// The most tokens a UTC month may use.
|
||||||
|
final int? monthlyTokens,
|
||||||
|
}) with Equatable {
|
||||||
|
/// No limits.
|
||||||
|
static const none = AssistantBudget();
|
||||||
|
|
||||||
|
/// Whether it has no limit at all.
|
||||||
|
bool get isNone =>
|
||||||
|
dailyCostMicros == null &&
|
||||||
|
monthlyCostMicros == null &&
|
||||||
|
dailyTokens == null &&
|
||||||
|
monthlyTokens == null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [
|
||||||
|
dailyCostMicros,
|
||||||
|
monthlyCostMicros,
|
||||||
|
dailyTokens,
|
||||||
|
monthlyTokens,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What a person used of all their assistants together in a UTC day or
|
||||||
|
/// month, as their budget counts it (D58).
|
||||||
|
final class const BudgetUse({
|
||||||
|
/// What it cost, in millionths of the currency.
|
||||||
|
final int costMicros = 0,
|
||||||
|
|
||||||
|
/// The tokens, of every assistant.
|
||||||
|
final int tokens = 0,
|
||||||
|
}) with Equatable {
|
||||||
|
/// Nothing used.
|
||||||
|
static const nothing = BudgetUse();
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [costMicros, tokens];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One of a budget's limits with what is left of it: the day's or the
|
||||||
|
/// month's, in money or tokens.
|
||||||
|
typedef BudgetLeft = ({
|
||||||
|
AssistantLimitUnit unit,
|
||||||
|
bool daily,
|
||||||
|
int limit,
|
||||||
|
int left,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The limits [budget] sets, the day's before the month's and money before
|
||||||
|
/// tokens, each with what is left after [today] and [thisMonth].
|
||||||
|
List<BudgetLeft> budgetLeft(
|
||||||
|
AssistantBudget budget, {
|
||||||
|
required BudgetUse today,
|
||||||
|
required BudgetUse thisMonth,
|
||||||
|
}) => [
|
||||||
|
for (final (unit, daily, limit, used) in [
|
||||||
|
(AssistantLimitUnit.money, true, budget.dailyCostMicros, today.costMicros),
|
||||||
|
(AssistantLimitUnit.tokens, true, budget.dailyTokens, today.tokens),
|
||||||
|
(
|
||||||
|
AssistantLimitUnit.money,
|
||||||
|
false,
|
||||||
|
budget.monthlyCostMicros,
|
||||||
|
thisMonth.costMicros,
|
||||||
|
),
|
||||||
|
(AssistantLimitUnit.tokens, false, budget.monthlyTokens, thisMonth.tokens),
|
||||||
|
])
|
||||||
|
if (limit != null)
|
||||||
|
(unit: unit, daily: daily, limit: limit, left: max(0, limit - used)),
|
||||||
|
];
|
||||||
|
|
||||||
/// An assistant the operator granted to the signed-in person (Halkyon
|
/// An assistant the operator granted to the signed-in person (Halkyon
|
||||||
/// Learn's ADR 0021, points 6 and 9): who answers, what is left of its
|
/// Learn's ADR 0021, points 6 and 9): who answers, how much it may think,
|
||||||
/// limits, and whether the person agreed to what goes where.
|
/// and whether the person agreed to what goes where.
|
||||||
final class const OfferedAssistant({
|
final class const OfferedAssistant({
|
||||||
/// The ID the operator gave it, which questions name.
|
/// The ID the operator gave it, which questions name.
|
||||||
required final String id,
|
required final String id,
|
||||||
|
|
@ -39,40 +123,17 @@ final class const OfferedAssistant({
|
||||||
/// until they do, or once it moved to another provider.
|
/// until they do, or once it moved to another provider.
|
||||||
final DateTime? agreedAt,
|
final DateTime? agreedAt,
|
||||||
|
|
||||||
/// What its limits and use are counted in.
|
/// The effort a question takes unless it names one (D63); null: the
|
||||||
final AssistantLimitUnit unit = AssistantLimitUnit.tokens,
|
/// model's own.
|
||||||
|
final LlmEffort? effort,
|
||||||
|
|
||||||
/// The most a UTC day may use, in [unit]; null for no daily limit.
|
/// The efforts a question may name, from the least; empty: none to
|
||||||
final int? dailyLimit,
|
/// choose.
|
||||||
|
final List<LlmEffort> efforts = const [],
|
||||||
/// The most a UTC month may use, in [unit]; null for no monthly limit.
|
|
||||||
final int? monthlyLimit,
|
|
||||||
|
|
||||||
/// What today used, in [unit].
|
|
||||||
final int usedToday = 0,
|
|
||||||
|
|
||||||
/// What this month used, in [unit].
|
|
||||||
final int usedThisMonth = 0,
|
|
||||||
|
|
||||||
/// Whether its limits were set in another unit than its pricing's now:
|
|
||||||
/// it answers nothing until the operator sets them again.
|
|
||||||
final bool outdated = false,
|
|
||||||
}) with Equatable {
|
}) with Equatable {
|
||||||
/// Whether the person agreed to what goes where.
|
/// Whether the person agreed to what goes where.
|
||||||
bool get agreed => agreedAt != null;
|
bool get agreed => agreedAt != null;
|
||||||
|
|
||||||
/// What is left of today's limit; null without one.
|
|
||||||
int? get leftToday => switch (dailyLimit) {
|
|
||||||
final limit? => max(0, limit - usedToday),
|
|
||||||
null => null,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// What is left of this month's limit; null without one.
|
|
||||||
int? get leftThisMonth => switch (monthlyLimit) {
|
|
||||||
final limit? => max(0, limit - usedThisMonth),
|
|
||||||
null => null,
|
|
||||||
};
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
id,
|
id,
|
||||||
|
|
@ -82,16 +143,13 @@ final class const OfferedAssistant({
|
||||||
model,
|
model,
|
||||||
images,
|
images,
|
||||||
agreedAt,
|
agreedAt,
|
||||||
unit,
|
effort,
|
||||||
dailyLimit,
|
efforts,
|
||||||
monthlyLimit,
|
|
||||||
usedToday,
|
|
||||||
usedThisMonth,
|
|
||||||
outdated,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The assistants the instance offers the signed-in person.
|
/// The assistants the instance offers the signed-in person, with their
|
||||||
|
/// budget for them together and what is used of it.
|
||||||
final class const AssistantsOffer({
|
final class const AssistantsOffer({
|
||||||
/// Those granted to them, in the operator's order.
|
/// Those granted to them, in the operator's order.
|
||||||
final List<OfferedAssistant> assistants = const [],
|
final List<OfferedAssistant> assistants = const [],
|
||||||
|
|
@ -101,12 +159,32 @@ final class const AssistantsOffer({
|
||||||
|
|
||||||
/// The currency of what is counted in money (ISO 4217).
|
/// The currency of what is counted in money (ISO 4217).
|
||||||
final String currency = 'USD',
|
final String currency = 'USD',
|
||||||
|
|
||||||
|
/// Their budget (D58); none: no limits.
|
||||||
|
final AssistantBudget budget = AssistantBudget.none,
|
||||||
|
|
||||||
|
/// What this UTC day used so far.
|
||||||
|
final BudgetUse usedToday = BudgetUse.nothing,
|
||||||
|
|
||||||
|
/// What this UTC month used so far.
|
||||||
|
final BudgetUse usedThisMonth = BudgetUse.nothing,
|
||||||
}) with Equatable {
|
}) with Equatable {
|
||||||
/// Nothing offered: an instance without assistants, or a member (D43).
|
/// Nothing offered: an instance without assistants, or a member (D43).
|
||||||
static const none = AssistantsOffer();
|
static const none = AssistantsOffer();
|
||||||
|
|
||||||
|
/// The budget's limits with what is left of each.
|
||||||
|
List<BudgetLeft> get left =>
|
||||||
|
budgetLeft(budget, today: usedToday, thisMonth: usedThisMonth);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [assistants, ownKeys, currency];
|
List<Object?> get props => [
|
||||||
|
assistants,
|
||||||
|
ownKeys,
|
||||||
|
currency,
|
||||||
|
budget,
|
||||||
|
usedToday,
|
||||||
|
usedThisMonth,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// What a person used of one assistant in one UTC month: their own, or,
|
/// What a person used of one assistant in one UTC month: their own, or,
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue