Skip to content

Commit 875772f

Browse files
Add -g for specifying a GBNF grammar. (#2)
1 parent a02ec68 commit 875772f

5 files changed

Lines changed: 51 additions & 95 deletions

File tree

Cargo.lock

Lines changed: 0 additions & 83 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ tracing = ["asimov-module/tracing", "clientele?/tracing"]
2929

3030
[dependencies]
3131
asimov-module = { version = "25.0.0-dev.21", default-features = false }
32-
bon = { version = "3.7.2", default-features = false, features = ["alloc"] }
3332
serde = { version = "1.0", default-features = false, features = ["alloc"] }
3433
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
3534
ureq = { version = "3.1", features = ["json"] }

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,19 @@ export ASIMOV_LLAMACPP_MODEL="TinyLlama-1.1B-Chat-v1.0"
5959
echo "Why is the sky blue?" | asimov-llamacpp-prompter
6060
```
6161

62+
### 🧪 Simple GBNF example
63+
64+
1) Create a tiny grammar that only allows `ok`:
65+
```bnf
66+
# only_ok.gbnf
67+
root ::= "ok" "\n"
68+
```
69+
2) Run the prompter with the grammar:
70+
```bash
71+
echo "Say anything." | asimov-llamacpp-prompter -g only_ok.gbnf
72+
# => ok
73+
```
74+
6275
## 👨‍💻 Development
6376

6477
```bash

src/lib.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,33 @@ use asimov_module::{prelude::*, tracing};
77
use core::error::Error;
88
use serde_json::{Value, json};
99

10-
#[derive(Clone, Debug, bon::Builder)]
11-
#[builder(on(String, into))]
10+
#[derive(Clone, Debug)]
1211
pub struct Options {
1312
/// Base URL of the llama.cpp server (e.g. http://localhost:8080)
1413
pub endpoint: String,
15-
1614
/// Model identifier (e.g. "llama3")
1715
pub model: String,
16+
/// Optional raw GBNF grammar text to constrain decoding
17+
pub grammar: Option<String>,
1818
}
1919

20-
/// Generate a response using llama.cpp’s OpenAI-compatible endpoint.
21-
/// Example: POST /v1/chat/completions
20+
/// POST /v1/chat/completions with optional GBNF grammar
2221
pub fn generate(input: impl AsRef<str>, options: &Options) -> Result<Vec<String>, Box<dyn Error>> {
2322
const UA: &str = "asimov-llamacpp-module";
2423
const CT_JSON: &str = "application/json";
2524

26-
let req = json!({
25+
let mut req = json!({
2726
"model": options.model,
2827
"messages": [{ "role": "user", "content": input.as_ref() }],
2928
"stream": false
3029
});
3130

31+
if let Some(g) = &options.grammar {
32+
if let Value::Object(obj) = &mut req {
33+
obj.insert("grammar".into(), Value::String(g.clone()));
34+
}
35+
}
36+
3237
let agent = ureq::Agent::config_builder()
3338
.http_status_as_error(false)
3439
.user_agent(UA)

src/prompter/main.rs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,19 @@ struct Options {
1515
#[clap(flatten)]
1616
flags: StandardOptions,
1717

18-
#[clap(long, short = 'm')]
18+
#[clap(long, short)]
1919
model: Option<String>,
2020

21+
/// Path to a GBNF grammar file (used to constrain decoding)
22+
#[clap(long, short)]
23+
grammar: Option<String>,
24+
25+
/// Input file (defaults to STDIN)
26+
#[clap(long, short)]
2127
input: Option<String>,
28+
29+
/// Output file (defaults to STDOUT)
30+
#[clap(long, short)]
2231
output: Option<String>,
2332
}
2433

@@ -88,10 +97,23 @@ pub fn main() -> Result<SysexitsError, Box<dyn Error>> {
8897
Box::new(std::io::stdout().lock())
8998
};
9099

91-
let options = asimov_llamacpp_module::Options::builder()
92-
.endpoint(endpoint)
93-
.model(model)
94-
.build();
100+
let grammar: Option<String> = if let Some(path) = options.grammar {
101+
match std::fs::read_to_string(&path) {
102+
Ok(s) => Some(s),
103+
Err(e) => {
104+
tracing::error!("unable to read grammar file '{}': {e}", path);
105+
return Ok(EX_NOINPUT);
106+
},
107+
}
108+
} else {
109+
None
110+
};
111+
112+
let options = asimov_llamacpp_module::Options {
113+
endpoint,
114+
model,
115+
grammar,
116+
};
95117

96118
let response = asimov_llamacpp_module::generate(&input, &options)?;
97119

0 commit comments

Comments
 (0)