tools-local-ask: send a local file and a question to a local Ollama model and print the answer. No cloud path -- infrastructure_ai::OllamaProvider only ever reaches the operator-allowlisted local sidecar (OLLAMA_ENDPOINTS).
| Tier | tools |
| Role | unclassified (baselined) |
| Path | crates/tools/local-ask |
| Edition | 2021 |
| Targets | tools-local-ask, tools_local_ask |
| Public items | 18 across 0 modules |
| Tests | 19 |
What it is for
Logic for tools-local-ask: config precedence, input bounding, prompt construction, and starting a local Ollama server if one isn't already running. Public so other tools in this repo (anything composing a sharder with a local-model question, e.g. a combined "shard this document and ask about it in one command" CLI) can reuse the exact same server-startup logic rather than reimplementing it -- see Rule 14.
Capabilities
verb:bound
Logic for tools-local-ask: config precedence, input bounding, prompt
| Item |
|---|
fn bound_input(text : & str, max_bytes : usize) ->(String, bool) |
verb:build
Logic for tools-local-ask: config precedence, input bounding, prompt
| Item |
|---|
fn build_prompt(question : & str, content : & str, truncated : bool) ->(String, String) |
fn build_review_prompt(context_label : & str, source : & str, truncated : bool) ->(String, String) |
verb:constants
Logic for tools-local-ask: config precedence, input bounding, prompt
| Item |
|---|
pub const DEFAULT_MODEL: & str |
pub const DEFAULT_OLLAMA_URL: & str |
pub const MAX_INPUT_BYTES: usize |
pub const SYSTEM_OLLAMA_MODELS_PATH: & str |
pub const OLLAMA_SYSTEMD_UNIT: & str |
pub const STARTUP_TIMEOUT: Duration |
pub const STARTUP_POLL_INTERVAL: Duration |
verb:ensure
Logic for tools-local-ask: config precedence, input bounding, prompt
| Item |
|---|
fn ensure_ollama_running(base_url : & str, ollama_bin : & str, process_env : & HashMap <String, String>,) |
verb:is
Logic for tools-local-ask: config precedence, input bounding, prompt
| Item |
|---|
fn is_listening(host : & str, port : u16) -> bool |
verb:parse
Logic for tools-local-ask: config precedence, input bounding, prompt
| Item |
|---|
fn parse_host_port(base_url : & str) -> Option <(String, u16)> |
verb:spawn
Logic for tools-local-ask: config precedence, input bounding, prompt
| Item |
|---|
fn spawn_ollama_serve(ollama_bin : & str, process_env : & HashMap <String, String>,) -> std::io::Result <()> |
verb:try
Logic for tools-local-ask: config precedence, input bounding, prompt
| Item |
|---|
fn try_systemctl_start(unit : & str) -> bool |
fn try_systemctl_start_via_sudo(unit : & str) -> bool |
Config
Logic for tools-local-ask: config precedence, input bounding, prompt
| Item |
|---|
pub struct Config |
fn resolve(cli_model : Option <& str>, cli_ollama_url : Option <& str>, process_env : & HashMap <String, String>,) -> Config |
How to use it
No examples/ target and no doctest in this crate's rustdoc. The tests listed under Verification are the closest executable usage.
Module structure
No public modules: the crate root is its whole surface.
Public surface
`crate root`
| Item | What it is |
|---|---|
pub const DEFAULT_MODEL: & str | Default local model -- matches the repo-wide default used by tools-prompt-shaper (general reasoning, not a coder model). |
pub const DEFAULT_OLLAMA_URL: & str | Default Ollama base URL, matching infrastructure_ai::OllamaProvider::local. |
pub const MAX_INPUT_BYTES: usize | Hard cap on the bytes read from the input file (Gate 1.5.6 input limit) |
pub struct Config | Resolved configuration for one run |
fn resolve(cli_model : Option <& str>, cli_ollama_url : Option <& str>, process_env : & HashMap <String, String>,) -> Config | Resolve config from explicit sources -- pure, no environment access itself, so main supplies the real process environment and every precedence rule is testable without running the binary. |
fn bound_input(text : & str, max_bytes : usize) ->(String, bool) | Truncate text to at most max_bytes, cutting at the last valid UTF-8 char boundary at or before the cap rather than panicking or splitting a multi-byte character |
fn build_prompt(question : & str, content : & str, truncated : bool) ->(String, String) | Build the (system_prompt, user_prompt) pair sent to the model |
fn build_review_prompt(context_label : & str, source : & str, truncated : bool) ->(String, String) | Build the (system_prompt, user_prompt) pair for --review mode: a fixed code-review question rather than an operator-supplied one, so a review round is always the same one-command call and its output is comparable run to run |
fn parse_host_port(base_url : & str) -> Option <(String, u16)> | Parse host:port out of a base URL (http://localhost:11434 -> ("localhost", 11434)), tolerating a missing scheme (localhost:11434) |
pub const SYSTEM_OLLAMA_MODELS_PATH: & str | Well-known system install layout for the Ollama systemd package on this class of machine (/usr/share/ollama/.ollama/models) |
pub const OLLAMA_SYSTEMD_UNIT: & str | Name of the systemd unit already configured on this class of machine (systemctl status ollama -- enabled, runs as the ollama user, reads the system model store) |
pub const STARTUP_TIMEOUT: Duration | How long to wait, total, for a self-started ollama serve to come up before giving up with a clear error instead of hanging forever. |
pub const STARTUP_POLL_INTERVAL: Duration | — |
fn is_listening(host : & str, port : u16) -> bool | Is anything listening on host:port? A bare TCP connect, not an HTTP call -- fast, and doesn't need the operator-allowlist machinery OllamaProvider itself enforces for the real request |
fn try_systemctl_start(unit : & str) -> bool | Try systemctl start <unit> with no output and no interactive prompt; true only if the command actually ran and exited success |
fn try_systemctl_start_via_sudo(unit : & str) -> bool | Same, escalated through sudo -n (non-interactive: fails immediately rather than hanging on a password prompt if no credential is cached) |
fn spawn_ollama_serve(ollama_bin : & str, process_env : & HashMap <String, String>,) -> std::io::Result <()> | Spawn ollama serve as an independent background process (strict argv, no shell) and return immediately -- it outlives this process, same as running it by hand in another terminal |
fn ensure_ollama_running(base_url : & str, ollama_bin : & str, process_env : & HashMap <String, String>,) | Ensure a server is reachable at base_url, starting one if not |
No pub use re-exports: every item above is declared in this crate.
Boundary
Reaches into infrastructure.
Shares tier tools with 84 other crates: tools-advisory-reach, tools-archive-guard, tools-artifact-scaffold, tools-ask-ai-core, tools-ask-ais, tools-ask-gemini, tools-book, tools-book-report, … (84 total).
_What this crate deliberately does NOT own is a judgment. No committed registry records one for it, so none is stated here._
Where it sits
| Tier (ontology) | tools |
| Architectural role (taxonomy) | unclassified (baselined) |
| Location | crates/tools/local-ask |
| Vocabulary in force (lexicon) | current |
Tier flow. Which tiers this crate's own edges cross.
flowchart LR n_tools["tools"] --> n_infrastructure["infrastructure"]
Dependencies
Runtime, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `infrastructure-ai` | infrastructure | no | always |
Runtime, from outside the workspace.
| Crate | Requirement | Features | Optional | Only on |
|---|---|---|---|---|
clap | ^4 | derive | no | always |
tokio | ^1 | full, macros, rt-multi-thread | no | always |
Development, in this workspace.
| Crate | Tier | Optional | Only on |
|---|---|---|---|
| `tools-cli-conformance` | tools | no | always |
Build. None.
Depended on by. 2 workspace crates.
Signal flow — what reaches this crate, and what it reaches.
flowchart LR n_tools_doc_ask["tools-doc-ask"] -->|uses| SELF n_tools_knowitall["tools-knowitall"] -->|uses| SELF SELF["tools-local-ask"] SELF -->|development| n_tools_cli_conformance["tools-cli-conformance"] SELF -->|runtime| n_infrastructure_ai["infrastructure-ai"] classDef self fill:#1f883d,stroke:#1f883d,color:#fff; class SELF self;
Feature flags
No Cargo features are defined: every capability is unconditional, so no consumer can receive a half-wired crate.
Targets
| Kind | Name | Source |
|---|---|---|
| bin | tools-local-ask | `src/main.rs` |
| lib | tools_local_ask | `src/lib.rs` |
Error model
No public error type was detected: no public item declares a type named *Error, and no public signature returns one.
Operational characteristics
| Property | Evidence |
|---|---|
| async public surface | none detected |
| async runtime | yes |
| database access | none detected |
| network I/O | none detected |
| unsafe code | none detected |
| environment variables | none detected |
No unsafe block, unsafe fn, unsafe impl or unsafe trait was found by the parser anywhere in this crate's source.
Configuration
No environment variable is read with a literal name anywhere in this crate. A variable whose key is computed at run time cannot be listed here, and is not claimed to be absent.
Related capabilities
2 workspace crates depend on this one: tools-doc-ask, tools-knowitall.
Verification
| Kind | Count |
|---|---|
| Unit tests | 19 |
| Integration tests | 0 |
| Examples | 0 |
| Doctests | 0 |
Evidence by module. How often each public module is named by something executable.
| Module | Tests | Examples | Consumers |
|---|---|---|---|
crate root | 18 | 0 | 10 |
What the tests establish, by name:
bound_input_cuts_at_a_char_boundary_not_mid_character—src/lib.rsbound_input_leaves_short_text_untouched—src/lib.rsbound_input_truncates_long_text_and_reports_it—src/lib.rsbuild_prompt_embeds_content_and_question—src/lib.rsbuild_prompt_notes_truncation_in_system_prompt_only_when_truncated—src/lib.rsbuild_review_prompt_embeds_label_and_source_and_asks_for_concrete_findings—src/lib.rsbuild_review_prompt_notes_truncation_only_when_truncated—src/lib.rsparse_host_port_handles_http_scheme—src/lib.rsparse_host_port_handles_https_scheme—src/lib.rsparse_host_port_ignores_a_trailing_path—src/lib.rsparse_host_port_rejects_garbage—src/lib.rsparse_host_port_rejects_missing_port—src/lib.rsparse_host_port_tolerates_missing_scheme—src/lib.rsreports_down_when_nothing_is_listening—src/lib.rsresolve_all_defaults_when_nothing_is_set—src/lib.rsresolve_cli_flag_overrides_env_and_default—src/lib.rsresolve_process_env_overrides_default—src/lib.rsresolves_the_localhost_hostname_not_just_a_literal_ip—src/lib.rscli_definition_conforms—src/main.rs
Documentation coverage
| Measure | Documented | Total |
|---|---|---|
| Public items with rustdoc | 17 | 18 |
Public modules with a //! block | 0 | 0 |
pie showData
title Public items with rustdoc
"Documented" : 17
"No rustdoc detected" : 1
Metrics
| Metric | Value |
|---|---|
| Rust source files | 2 |
| Source lines | 614 |
| Code lines | 427 |
| Public API items | 18 |
| Public modules | 0 |
| Tests | 19 |
| Examples | 0 |
| Cargo features | 0 |
| Direct runtime dependencies | 3 |
| Workspace reverse dependencies | 2 |
pie showData
title Public API by kind
"constant" : 7
"function" : 10
"struct" : 1
pie showData
title Rust source composition
"Code" : 427
"Blank or comment" : 187
Generation
Rendered by tools-corpus corpus readme from repository evidence alone, renderer schema 2, lexicon current. No model, network service or database was consulted. Regenerate with tools-corpus corpus readme --write; verify with --check.