feat(learn): a private studyset goes to an assistant only once agreed
ADR 0021, D9 and D17. Before a question sends a private studyset's
content to a remote model, through the parts chosen or a wider reach
with private studysets switched on, a warning names the studysets, the
provider's host and its terms (where checked: Anthropic, OpenAI,
Google, OpenRouter), and is asked once per studyset and assistant: the
agreement is kept in the person's sealed settings (schema 13,
privateAgreements, folded only where both rows hold it), shown in the
studyset's privacy dialog with Take back, and marked on the folded
button while it holds. Follow-ups and Try again ask again once taken
back, as the scope keeps where the parts came from (partsFrom). No
warning for an own assistant on this machine (loopback or localhost,
halkyon_llm's LlmEndpoint.onThisMachine); the local network may be
shared.
Texts that step 3 made untrue: the consent sheet now says what an
assistant looks up goes too and that conversations are kept encrypted
for the person's devices; the privacy dialogs no longer mention
showing a studyset to Claude; the privacy page says what is looked up
and when a private studyset goes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 21:00:39 +01:00
|
|
|
import 'package:halkyon_llm/halkyon_llm.dart';
|
|
|
|
|
import 'package:test/test.dart';
|
|
|
|
|
|
|
|
|
|
LlmEndpoint _at(String baseUrl) => LlmEndpoint(
|
|
|
|
|
protocol: LlmProtocol.openai,
|
|
|
|
|
model: 'm',
|
|
|
|
|
apiKey: '',
|
|
|
|
|
baseUrl: baseUrl,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
test("takes the protocol's own address where none is given", () {
|
|
|
|
|
final endpoint = LlmEndpoint(
|
|
|
|
|
protocol: LlmProtocol.anthropic,
|
|
|
|
|
model: 'm',
|
|
|
|
|
apiKey: 'k',
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(endpoint.host, 'api.anthropic.com');
|
|
|
|
|
expect(endpoint.onThisMachine, isFalse);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('refuses http beyond the same machine or network', () {
|
|
|
|
|
expect(() => _at('http://example.com/v1'), throwsFormatException);
|
|
|
|
|
expect(_at('http://192.168.1.20:11434/v1').host, '192.168.1.20');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('knows a model on this machine: a loopback address or localhost '
|
|
|
|
|
'(ADR 0021, D9)', () {
|
|
|
|
|
for (final url in [
|
|
|
|
|
'http://127.0.0.1:11434/v1',
|
|
|
|
|
'http://127.8.0.1/v1',
|
|
|
|
|
'http://[::1]:8080/v1',
|
|
|
|
|
'http://localhost:11434/v1',
|
|
|
|
|
'http://ollama.localhost/v1',
|
|
|
|
|
'https://LOCALHOST/v1',
|
|
|
|
|
]) {
|
|
|
|
|
expect(_at(url).onThisMachine, isTrue, reason: url);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('takes nothing else for this machine: the network may be shared', () {
|
|
|
|
|
for (final url in [
|
|
|
|
|
'http://192.168.1.20:11434/v1',
|
|
|
|
|
'http://10.0.0.2/v1',
|
|
|
|
|
'http://[fd00::2]/v1',
|
|
|
|
|
'http://ollama/v1',
|
|
|
|
|
'http://gpu.local/v1',
|
|
|
|
|
'https://localhost.example.com/v1',
|
|
|
|
|
'https://api.openai.com/v1',
|
|
|
|
|
]) {
|
|
|
|
|
expect(_at(url).onThisMachine, isFalse, reason: url);
|
|
|
|
|
}
|
|
|
|
|
});
|
feat(llm): a request carries how much the model thinks, in each provider's field
LlmRequest.effort (LlmEffort: low, medium, high, xhigh, max, Anthropic's
names) goes as Anthropic's output_config.effort, as reasoning.effort to
the Responses API and OpenRouter, and as reasoning_effort to OpenAI's chat
completions, Gemini and any other host. endpoint.efforts says which
levels a host takes (Gemini low to high, Mistral none); send refuses
another before anything goes, the fake client too. LlmEffort.outputCeiling
gives the room for output each level wants, as thinking counts towards
the ceiling (Halkyon Learn's ADR 0021, D63).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-26 14:53:48 +01:00
|
|
|
|
|
|
|
|
group('effort (D63)', () {
|
|
|
|
|
test('runs from low to max, by name', () {
|
|
|
|
|
expect(LlmEffort.values.map((effort) => effort.key), [
|
|
|
|
|
'low',
|
|
|
|
|
'medium',
|
|
|
|
|
'high',
|
|
|
|
|
'xhigh',
|
|
|
|
|
'max',
|
|
|
|
|
]);
|
|
|
|
|
expect(LlmEffort.named('xhigh'), LlmEffort.xhigh);
|
|
|
|
|
expect(LlmEffort.named('XHIGH'), isNull);
|
|
|
|
|
expect(LlmEffort.named('minimal'), isNull);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('leaves more room for output the higher it is: what the model thinks '
|
|
|
|
|
'counts towards the ceiling', () {
|
|
|
|
|
expect(LlmEffort.values.map((effort) => effort.outputCeiling), [
|
|
|
|
|
8192,
|
|
|
|
|
12288,
|
|
|
|
|
16384,
|
|
|
|
|
32768,
|
|
|
|
|
65536,
|
|
|
|
|
]);
|
|
|
|
|
expect(LlmEffort.defaultOutputCeiling, LlmEffort.high.outputCeiling);
|
|
|
|
|
});
|
|
|
|
|
});
|
feat(learn): a private studyset goes to an assistant only once agreed
ADR 0021, D9 and D17. Before a question sends a private studyset's
content to a remote model, through the parts chosen or a wider reach
with private studysets switched on, a warning names the studysets, the
provider's host and its terms (where checked: Anthropic, OpenAI,
Google, OpenRouter), and is asked once per studyset and assistant: the
agreement is kept in the person's sealed settings (schema 13,
privateAgreements, folded only where both rows hold it), shown in the
studyset's privacy dialog with Take back, and marked on the folded
button while it holds. Follow-ups and Try again ask again once taken
back, as the scope keeps where the parts came from (partsFrom). No
warning for an own assistant on this machine (loopback or localhost,
halkyon_llm's LlmEndpoint.onThisMachine); the local network may be
shared.
Texts that step 3 made untrue: the consent sheet now says what an
assistant looks up goes too and that conversations are kept encrypted
for the person's devices; the privacy dialogs no longer mention
showing a studyset to Claude; the privacy page says what is looked up
and when a private studyset goes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-25 21:00:39 +01:00
|
|
|
}
|