diff --git a/packages/genkit_llamadart/doc/usage.md b/packages/genkit_llamadart/doc/usage.md index 7fdbeade3..3d2aac7d9 100644 --- a/packages/genkit_llamadart/doc/usage.md +++ b/packages/genkit_llamadart/doc/usage.md @@ -426,8 +426,10 @@ block; zero closes the block immediately. Enable thinking separately with `startTag`/`endTag` from the selected template; custom delimiters and `forcedMessage` pass through unchanged, except that on Qwen3-Coder XML templates (Qwen3.5) with no `endTag` set, a `\n` is appended to `forcedMessage` -before ``. Budgets need runtime support and cannot -be combined with media or speculative decoding. +before ``; an unset `forcedMessage` becomes a lone `\n`. Backends that +take chat messages, such as LiteRT-LM, close the thought themselves and get no +`\n`. Budgets need runtime support and cannot be combined with media or +speculative decoding. `reusePromptPrefix: false` disables native llama.cpp prefix reuse. It does not create an isolated conversation: always pass the complete history. The adapter diff --git a/packages/genkit_llamadart/test/integration/genkit/actions/model_action/uses_structured_generation_for_constrained_json_output_test.dart b/packages/genkit_llamadart/test/integration/genkit/actions/model_action/uses_structured_generation_for_constrained_json_output_test.dart index fc382ed84..0719e9dc3 100644 --- a/packages/genkit_llamadart/test/integration/genkit/actions/model_action/uses_structured_generation_for_constrained_json_output_test.dart +++ b/packages/genkit_llamadart/test/integration/genkit/actions/model_action/uses_structured_generation_for_constrained_json_output_test.dart @@ -13,6 +13,7 @@ void main() { ..templateResult = const llama.LlamaChatTemplateResult( prompt: 'prompt', format: 0, + grammar: 'root ::= "answer"', grammarTriggers: [ llama.GrammarTrigger(type: 0, value: ''), llama.GrammarTrigger(type: 1, value: '42', token: 42), @@ -47,6 +48,7 @@ void main() { expect(runtime.chatTemplateCallCount, 1); expect(runtime.generateCallCount, 1); + expect(runtime.lastGenerationParams!.grammar, 'root ::= "answer"'); expect( runtime.lastGenerationParams!.grammarTriggers.map( (trigger) => trigger.triggerType, diff --git a/test/ci/test_genkit_release.py b/test/ci/test_genkit_release.py index 9609093b4..c7e3297f3 100644 --- a/test/ci/test_genkit_release.py +++ b/test/ci/test_genkit_release.py @@ -188,6 +188,14 @@ def test_version_or_changelog_mismatch_fails(self): (self.root / "packages/genkit_llamadart/CHANGELOG.md").write_text(heading + "\n") self.run_step("Validate release state", success=False) + def test_changelog_date_must_be_iso(self): + self.env.update(GITHUB_REF_TYPE="tag", GITHUB_REF_NAME="genkit_llamadart-v1.5.0", GITHUB_REF="refs/tags/genkit_llamadart-v1.5.0") + changelog = self.root / "packages/genkit_llamadart/CHANGELOG.md" + for heading in ["## 1.5.0 - 2026-9-28", "## 1.5.0 - soon", "## 1.5.0 - 2026-09-28T00:00", "## 1.5.0 - 2026-09-28 extra"]: + changelog.write_text(heading + "\n") + self.run_step("Validate release state", success=False) + self.run_step("Verify package-specific stable tag", success=False, workflow="publish_genkit_pubdev.yml") + def test_published_version_requires_tag_and_skips_dispatch(self): self.scenario["pub_http"] = "200" self.run_step("Validate release state", success=False) diff --git a/test/unit/hook/build_hook_litert_lm_integration_test.dart b/test/unit/hook/build_hook_litert_lm_integration_test.dart index 491144d3e..53cecbd21 100644 --- a/test/unit/hook/build_hook_litert_lm_integration_test.dart +++ b/test/unit/hook/build_hook_litert_lm_integration_test.dart @@ -1081,9 +1081,19 @@ dependency_overrides: {llamadart_llama_cpp_flutter: {path: resolved companion}} final previousTemplateIsCurrent = hookTemplateHash('_previousLiteRtLmCompanionManifestTemplateSha256') == hookTemplateHash('_liteRtLmCompanionManifestTemplateSha256'); - final previousFixtureOnCoreLine = previousFixturePin.startsWith( - '$coreUpstream-', + final previousFixtureRebuild = _rebuildOnLine( + previousFixturePin, + coreUpstream, ); + final previousFixtureOnCoreLine = previousFixtureRebuild != null; + + test('a bare tag is rebuild 0 of its own release line only', () { + expect(_rebuildOnLine('v0.20.0', 'v0.20.0'), 0); + expect(_rebuildOnLine('v0.20.0-3', 'v0.20.0'), 3); + expect(_rebuildOnLine('v0.20.1', 'v0.20.0'), isNull); + expect(_rebuildOnLine('v0.20.10', 'v0.20.1'), isNull); + expect(_rebuildOnLine('v0.20.1-2', 'v0.20.0'), isNull); + }); test('previous companion template fixture matches the hook hash and, on the ' 'core release line, is an earlier rebuild', () { @@ -1099,10 +1109,9 @@ dependency_overrides: {llamadart_llama_cpp_flutter: {path: resolved companion}} ).firstMatch(hookSource)!.group(1); expect(sha256.convert(utf8.encode(template)).toString(), expected); - if (previousFixtureOnCoreLine) { + if (previousFixtureRebuild != null) { expect( - int.parse(previousFixturePin.substring(coreUpstream.length + 1)) < - coreRebuild, + previousFixtureRebuild < coreRebuild, isTrue, reason: 'the fixture pins $previousFixturePin, not an earlier build of ' @@ -2936,3 +2945,9 @@ Future _writeBundleLibraries( await File(path.join(bundleDir.path, name)).writeAsString('fake-$name'); } } + +int? _rebuildOnLine(String pin, String upstream) { + if (pin == upstream) return 0; + if (!pin.startsWith('$upstream-')) return null; + return int.tryParse(pin.substring(upstream.length + 1)); +} diff --git a/test/unit/tooling/ci_workflow_selection_test.dart b/test/unit/tooling/ci_workflow_selection_test.dart index 0ef2938c9..5a27fef5b 100644 --- a/test/unit/tooling/ci_workflow_selection_test.dart +++ b/test/unit/tooling/ci_workflow_selection_test.dart @@ -1536,13 +1536,42 @@ for event,flag,native,cancel,good,main in itertools.product(('push','pull_reques {'run': 'npx playwright install-deps'}, {'uses': 'awalsh128/cache-apt-pkgs-action@v1'}, {'uses': 'ConorMacBride/install-linux-deps@v1'}, + { + 'uses': 'ConorMacBride/install-package@v1', + 'with': {'apt': 'libfoo'}, + }, + { + 'uses': 'some/setup@v1', + 'with': {'pre-script': 'sudo apt-get update'}, + }, ]) { expect(_installsSystemPackages(step), isTrue, reason: '$step'); } + final root = Directory.systemTemp.createTempSync('apt_action_'); + addTearDown(() => root.deleteSync(recursive: true)); + Directory('${root.path}/local').createSync(); + File('${root.path}/local/action.yml').writeAsStringSync(''' +runs: + using: composite + steps: + - run: echo ok + - run: sudo apt-get install -y libfoo + shell: bash +'''); + expect( + _installsSystemPackages({'uses': './local'}, root: root.path), + isTrue, + ); for (final step in [ {'run': 'npx playwright install chromium'}, {'uses': 'actions/checkout@v7'}, {'uses': 'subosito/flutter-action@v2'}, + {'uses': 'actions/setup-captain@v1'}, + { + 'uses': 'actions/checkout@v7', + 'with': {'path': 'notes'}, + }, + {'uses': './.github/actions/companion-package'}, ]) { expect(_installsSystemPackages(step), isFalse, reason: '$step'); } @@ -1889,8 +1918,37 @@ for event,flag,native,cancel,good,main in itertools.product(('push','pull_reques /// against. const _pinnedCoverageVersion = '1.16.0'; -bool _installsSystemPackages(Map step) => - RegExp( - r'\bapt(-get)?\b|dpkg|install-deps|--with-deps', - ).hasMatch('${step['run']}') || - RegExp(r'apt|dpkg|install-.*deps').hasMatch('${step['uses']}'); +final _systemPackageCommand = RegExp( + r'\bapt(-get)?\b|\bdpkg\b|install-deps|--with-deps', +); +final _systemPackageAction = RegExp( + r'(^|[/@_-])(apt|apt-get|dpkg)([/@_-]|$)|install-.*deps', +); + +/// Whether [step] installs system packages through a command, an action name, +/// an `apt`-style `with:` input or a local composite action. +bool _installsSystemPackages(Map step, {String root = '.'}) { + if (_systemPackageCommand.hasMatch('${step['run']}')) return true; + final uses = '${step['uses'] ?? ''}'; + if (_systemPackageAction.hasMatch(uses)) return true; + final inputs = step['with']; + if (inputs is Map) { + for (final entry in inputs.entries) { + if (_systemPackageAction.hasMatch('${entry.key}') || + _systemPackageCommand.hasMatch('${entry.value}')) { + return true; + } + } + } + if (uses.startsWith('./')) { + final action = File('$root/$uses/action.yml'); + if (!action.existsSync()) return false; + final steps = + (loadYaml(action.readAsStringSync()) as Map)['runs']?['steps']; + return steps is List && + steps.any( + (nested) => _installsSystemPackages(nested as Map, root: root), + ); + } + return false; +} diff --git a/website/docs/guides/generation-and-streaming.md b/website/docs/guides/generation-and-streaming.md index 1d0432b2c..38d153651 100644 --- a/website/docs/guides/generation-and-streaming.md +++ b/website/docs/guides/generation-and-streaming.md @@ -354,8 +354,8 @@ forced text is a line break before the end delimiter, as llama.cpp forces it. On native llama.cpp, a grammar that takes the template-opened thought itself (its root starts with `think-body`, as for `responseFormat` on Qwen3.5) stays applied while the model reasons, so it also constrains the reply after a spent -budget. Any other grammar, including a caller `grammar` or a lazy grammar, -waits until the thought closes. WebGPU still pauses every grammar during +budget. Other grammars, such as a typical caller `grammar` or a lazy grammar, +wait until the thought closes. WebGPU still pauses every grammar during budgeted reasoning ([#1954](https://github.com/leehack/llamadart/issues/1954)). Native llama.cpp, WebGPU bridges that report `thinkingBudget` and native diff --git a/website/docs/maintainers/release-workflow.md b/website/docs/maintainers/release-workflow.md index 9c85393a9..b3c1188ee 100644 --- a/website/docs/maintainers/release-workflow.md +++ b/website/docs/maintainers/release-workflow.md @@ -497,13 +497,15 @@ pub.dev rejects an upload whose `CHANGELOG.md`, `README.md`, `LICENSE` or Example-tab file is over 256 KiB (262,144 bytes, `maxContentLength` in pub-dev's `pub_package_reader`), and it does so only after the release tag is pushed: `v0.19.2` was tagged but never published for this reason. It likewise -rejects a `pubspec.yaml` over 128 KiB (131,072 bytes, `maxPubspecLength`). +rejects a `pubspec.yaml` over 128 KiB (131,072 bytes, an inline `128 * 1024` +in the same reader). `verify_release_docs_versions.dart` checks those files in the core, every Apple companion and `genkit_llamadart`, in PR CI and in `release_on_prep_merge.yml` before any tag. It fails a file over the limit and warns above 90% of it. Genkit joins only this size check: its version and -release routing stay independent -([Genkit package](./genkit-package.md)). +release routing stay independent ([Genkit package](./genkit-package.md)). A +genkit-only PR and a genkit release do not run it, so check the +`genkit_llamadart` sizes by hand when preparing its release. `CHANGELOG.md` keeps every release from 0.10.0 on; older ones are in `doc/changelog_archive.md`, which pub does not package (`/doc/` is in