pythonai-grounded-generator 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pythonai_grounded_generator-0.1.0/PKG-INFO +88 -0
- pythonai_grounded_generator-0.1.0/README.md +74 -0
- pythonai_grounded_generator-0.1.0/pyproject.toml +27 -0
- pythonai_grounded_generator-0.1.0/setup.cfg +4 -0
- pythonai_grounded_generator-0.1.0/src/pythonai_grounded/__init__.py +5 -0
- pythonai_grounded_generator-0.1.0/src/pythonai_grounded/core.py +91 -0
- pythonai_grounded_generator-0.1.0/src/pythonai_grounded/py.typed +0 -0
- pythonai_grounded_generator-0.1.0/src/pythonai_grounded_generator.egg-info/PKG-INFO +88 -0
- pythonai_grounded_generator-0.1.0/src/pythonai_grounded_generator.egg-info/SOURCES.txt +11 -0
- pythonai_grounded_generator-0.1.0/src/pythonai_grounded_generator.egg-info/dependency_links.txt +1 -0
- pythonai_grounded_generator-0.1.0/src/pythonai_grounded_generator.egg-info/requires.txt +2 -0
- pythonai_grounded_generator-0.1.0/src/pythonai_grounded_generator.egg-info/top_level.txt +1 -0
- pythonai_grounded_generator-0.1.0/tests/test_core.py +95 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pythonai-grounded-generator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight grounded text generation from retrieved context
|
|
5
|
+
Keywords: llm,rag,grounded-generation,transformers,qwen
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: torch>=2.2
|
|
13
|
+
Requires-Dist: transformers>=4.51.0
|
|
14
|
+
|
|
15
|
+
# PythonAI Grounded Generator
|
|
16
|
+
|
|
17
|
+
A compact Python helper for generating answers from retrieved context with an already loaded Hugging Face causal language model.
|
|
18
|
+
|
|
19
|
+
It provides:
|
|
20
|
+
|
|
21
|
+
- Custom system prompts and questions
|
|
22
|
+
- Token-budget-aware context truncation
|
|
23
|
+
- Qwen3 non-thinking chat formatting
|
|
24
|
+
- CPU or CUDA inference (device controlled by the caller)
|
|
25
|
+
- A plain-text answer suitable for wrapping in application-specific Pydantic models
|
|
26
|
+
|
|
27
|
+
Retrieval, context assembly, and structured JSON serialization remain the responsibility of the calling application. This package does **not** guarantee factual correctness or replace source validation.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
After publication:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install pythonai-grounded-generator
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or install the local project:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Example
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
|
47
|
+
from pythonai_grounded import generate_answer
|
|
48
|
+
|
|
49
|
+
name = "Qwen/Qwen3-0.6B"
|
|
50
|
+
tokenizer = AutoTokenizer.from_pretrained(name)
|
|
51
|
+
model = AutoModelForCausalLM.from_pretrained(name).to("cpu")
|
|
52
|
+
model.eval()
|
|
53
|
+
|
|
54
|
+
answer = generate_answer(
|
|
55
|
+
model=model,
|
|
56
|
+
tokenizer=tokenizer,
|
|
57
|
+
question="Which endpoint loads an adapter?",
|
|
58
|
+
context="Use POST /v1/load_lora_adapter to load an adapter.",
|
|
59
|
+
system_prompt="Answer only from the context. Copy technical names exactly.",
|
|
60
|
+
max_new_tokens=64,
|
|
61
|
+
device="cpu",
|
|
62
|
+
)
|
|
63
|
+
print(answer)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## API
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
generate_answer(
|
|
70
|
+
model,
|
|
71
|
+
tokenizer,
|
|
72
|
+
question: str,
|
|
73
|
+
context: str,
|
|
74
|
+
system_prompt: str,
|
|
75
|
+
max_new_tokens: int = 64,
|
|
76
|
+
device: str = "cpu",
|
|
77
|
+
) -> str
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The supplied `model` and `tokenizer` must be compatible. The model must already be on the requested device and in evaluation mode. Model context limits are read from `model.config.max_position_embeddings`, falling back to 32768 if absent. Check actual model capabilities when switching models.
|
|
81
|
+
|
|
82
|
+
## Build
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv build
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The distributions are created in `dist/`.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# PythonAI Grounded Generator
|
|
2
|
+
|
|
3
|
+
A compact Python helper for generating answers from retrieved context with an already loaded Hugging Face causal language model.
|
|
4
|
+
|
|
5
|
+
It provides:
|
|
6
|
+
|
|
7
|
+
- Custom system prompts and questions
|
|
8
|
+
- Token-budget-aware context truncation
|
|
9
|
+
- Qwen3 non-thinking chat formatting
|
|
10
|
+
- CPU or CUDA inference (device controlled by the caller)
|
|
11
|
+
- A plain-text answer suitable for wrapping in application-specific Pydantic models
|
|
12
|
+
|
|
13
|
+
Retrieval, context assembly, and structured JSON serialization remain the responsibility of the calling application. This package does **not** guarantee factual correctness or replace source validation.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
After publication:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install pythonai-grounded-generator
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or install the local project:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install .
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Example
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
|
33
|
+
from pythonai_grounded import generate_answer
|
|
34
|
+
|
|
35
|
+
name = "Qwen/Qwen3-0.6B"
|
|
36
|
+
tokenizer = AutoTokenizer.from_pretrained(name)
|
|
37
|
+
model = AutoModelForCausalLM.from_pretrained(name).to("cpu")
|
|
38
|
+
model.eval()
|
|
39
|
+
|
|
40
|
+
answer = generate_answer(
|
|
41
|
+
model=model,
|
|
42
|
+
tokenizer=tokenizer,
|
|
43
|
+
question="Which endpoint loads an adapter?",
|
|
44
|
+
context="Use POST /v1/load_lora_adapter to load an adapter.",
|
|
45
|
+
system_prompt="Answer only from the context. Copy technical names exactly.",
|
|
46
|
+
max_new_tokens=64,
|
|
47
|
+
device="cpu",
|
|
48
|
+
)
|
|
49
|
+
print(answer)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## API
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
generate_answer(
|
|
56
|
+
model,
|
|
57
|
+
tokenizer,
|
|
58
|
+
question: str,
|
|
59
|
+
context: str,
|
|
60
|
+
system_prompt: str,
|
|
61
|
+
max_new_tokens: int = 64,
|
|
62
|
+
device: str = "cpu",
|
|
63
|
+
) -> str
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The supplied `model` and `tokenizer` must be compatible. The model must already be on the requested device and in evaluation mode. Model context limits are read from `model.config.max_position_embeddings`, falling back to 32768 if absent. Check actual model capabilities when switching models.
|
|
67
|
+
|
|
68
|
+
## Build
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
uv build
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The distributions are created in `dist/`.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pythonai-grounded-generator"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Lightweight grounded text generation from retrieved context"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"torch>=2.2",
|
|
13
|
+
"transformers>=4.51.0",
|
|
14
|
+
]
|
|
15
|
+
keywords = ["llm", "rag", "grounded-generation", "transformers", "qwen"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.packages.find]
|
|
24
|
+
where = ["src"]
|
|
25
|
+
|
|
26
|
+
[tool.setuptools.package-data]
|
|
27
|
+
pythonai_grounded = ["py.typed"]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Grounded answer generation with configurable prompts and token budgets."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import torch
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def generate_answer(
|
|
9
|
+
model: Any,
|
|
10
|
+
tokenizer: Any,
|
|
11
|
+
question: str,
|
|
12
|
+
context: str,
|
|
13
|
+
system_prompt: str,
|
|
14
|
+
max_new_tokens: int = 64,
|
|
15
|
+
device: str = "cpu",
|
|
16
|
+
) -> str:
|
|
17
|
+
"""Generate an answer using a preloaded model and retrieved context.
|
|
18
|
+
|
|
19
|
+
The caller owns retrieval, augmentation, model loading, and output schema.
|
|
20
|
+
This function formats the prompt, truncates context to the token budget,
|
|
21
|
+
generates an answer, and returns only the decoded completion.
|
|
22
|
+
|
|
23
|
+
Raises:
|
|
24
|
+
ValueError: If inputs are invalid or the prompt exceeds the budget.
|
|
25
|
+
"""
|
|
26
|
+
if not question.strip() or not system_prompt.strip():
|
|
27
|
+
raise ValueError("Question and system prompt must not be empty")
|
|
28
|
+
if max_new_tokens <= 0:
|
|
29
|
+
raise ValueError("max_new_tokens must be positive")
|
|
30
|
+
|
|
31
|
+
user_prefix = "Retrieved context:\n"
|
|
32
|
+
user_suffix = (
|
|
33
|
+
f"\n\nQuestion:\n{question}\n\n"
|
|
34
|
+
"Important: copy technical identifiers exactly as written "
|
|
35
|
+
"in the retrieved context."
|
|
36
|
+
)
|
|
37
|
+
messages = [
|
|
38
|
+
{"role": "system", "content": system_prompt},
|
|
39
|
+
{"role": "user", "content": user_prefix + user_suffix},
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# Count tokens needed even when the retrieved context is empty.
|
|
43
|
+
fixed_ids = tokenizer.apply_chat_template(
|
|
44
|
+
messages,
|
|
45
|
+
tokenize=True,
|
|
46
|
+
return_dict=False,
|
|
47
|
+
add_generation_prompt=True,
|
|
48
|
+
enable_thinking=False,
|
|
49
|
+
)
|
|
50
|
+
model_limit = getattr(model.config, "max_position_embeddings", 32768)
|
|
51
|
+
budget = model_limit - len(fixed_ids) - max_new_tokens - 32
|
|
52
|
+
if budget <= 0:
|
|
53
|
+
raise ValueError("Not enough token budget for the prompt")
|
|
54
|
+
|
|
55
|
+
context_ids = tokenizer(
|
|
56
|
+
context,
|
|
57
|
+
add_special_tokens=False,
|
|
58
|
+
truncation=True,
|
|
59
|
+
max_length=budget,
|
|
60
|
+
)["input_ids"]
|
|
61
|
+
trimmed_context = tokenizer.decode(context_ids, skip_special_tokens=True)
|
|
62
|
+
messages[1]["content"] = user_prefix + trimmed_context + user_suffix
|
|
63
|
+
|
|
64
|
+
prompt = tokenizer.apply_chat_template(
|
|
65
|
+
messages,
|
|
66
|
+
tokenize=False,
|
|
67
|
+
add_generation_prompt=True,
|
|
68
|
+
enable_thinking=False,
|
|
69
|
+
)
|
|
70
|
+
inputs = tokenizer(
|
|
71
|
+
prompt,
|
|
72
|
+
return_tensors="pt",
|
|
73
|
+
add_special_tokens=False,
|
|
74
|
+
).to(device)
|
|
75
|
+
input_length = inputs["input_ids"].shape[1]
|
|
76
|
+
if input_length + max_new_tokens > model_limit:
|
|
77
|
+
raise ValueError("Prompt exceeds the model token budget")
|
|
78
|
+
|
|
79
|
+
with torch.inference_mode():
|
|
80
|
+
output = model.generate(
|
|
81
|
+
**inputs,
|
|
82
|
+
max_new_tokens=max_new_tokens,
|
|
83
|
+
do_sample=False,
|
|
84
|
+
eos_token_id=tokenizer.eos_token_id,
|
|
85
|
+
pad_token_id=tokenizer.eos_token_id,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return tokenizer.decode(
|
|
89
|
+
output[0][input_length:],
|
|
90
|
+
skip_special_tokens=True,
|
|
91
|
+
).strip()
|
|
File without changes
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pythonai-grounded-generator
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight grounded text generation from retrieved context
|
|
5
|
+
Keywords: llm,rag,grounded-generation,transformers,qwen
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: torch>=2.2
|
|
13
|
+
Requires-Dist: transformers>=4.51.0
|
|
14
|
+
|
|
15
|
+
# PythonAI Grounded Generator
|
|
16
|
+
|
|
17
|
+
A compact Python helper for generating answers from retrieved context with an already loaded Hugging Face causal language model.
|
|
18
|
+
|
|
19
|
+
It provides:
|
|
20
|
+
|
|
21
|
+
- Custom system prompts and questions
|
|
22
|
+
- Token-budget-aware context truncation
|
|
23
|
+
- Qwen3 non-thinking chat formatting
|
|
24
|
+
- CPU or CUDA inference (device controlled by the caller)
|
|
25
|
+
- A plain-text answer suitable for wrapping in application-specific Pydantic models
|
|
26
|
+
|
|
27
|
+
Retrieval, context assembly, and structured JSON serialization remain the responsibility of the calling application. This package does **not** guarantee factual correctness or replace source validation.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
After publication:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install pythonai-grounded-generator
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Or install the local project:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Example
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
|
47
|
+
from pythonai_grounded import generate_answer
|
|
48
|
+
|
|
49
|
+
name = "Qwen/Qwen3-0.6B"
|
|
50
|
+
tokenizer = AutoTokenizer.from_pretrained(name)
|
|
51
|
+
model = AutoModelForCausalLM.from_pretrained(name).to("cpu")
|
|
52
|
+
model.eval()
|
|
53
|
+
|
|
54
|
+
answer = generate_answer(
|
|
55
|
+
model=model,
|
|
56
|
+
tokenizer=tokenizer,
|
|
57
|
+
question="Which endpoint loads an adapter?",
|
|
58
|
+
context="Use POST /v1/load_lora_adapter to load an adapter.",
|
|
59
|
+
system_prompt="Answer only from the context. Copy technical names exactly.",
|
|
60
|
+
max_new_tokens=64,
|
|
61
|
+
device="cpu",
|
|
62
|
+
)
|
|
63
|
+
print(answer)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## API
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
generate_answer(
|
|
70
|
+
model,
|
|
71
|
+
tokenizer,
|
|
72
|
+
question: str,
|
|
73
|
+
context: str,
|
|
74
|
+
system_prompt: str,
|
|
75
|
+
max_new_tokens: int = 64,
|
|
76
|
+
device: str = "cpu",
|
|
77
|
+
) -> str
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The supplied `model` and `tokenizer` must be compatible. The model must already be on the requested device and in evaluation mode. Model context limits are read from `model.config.max_position_embeddings`, falling back to 32768 if absent. Check actual model capabilities when switching models.
|
|
81
|
+
|
|
82
|
+
## Build
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv build
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The distributions are created in `dist/`.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/pythonai_grounded/__init__.py
|
|
4
|
+
src/pythonai_grounded/core.py
|
|
5
|
+
src/pythonai_grounded/py.typed
|
|
6
|
+
src/pythonai_grounded_generator.egg-info/PKG-INFO
|
|
7
|
+
src/pythonai_grounded_generator.egg-info/SOURCES.txt
|
|
8
|
+
src/pythonai_grounded_generator.egg-info/dependency_links.txt
|
|
9
|
+
src/pythonai_grounded_generator.egg-info/requires.txt
|
|
10
|
+
src/pythonai_grounded_generator.egg-info/top_level.txt
|
|
11
|
+
tests/test_core.py
|
pythonai_grounded_generator-0.1.0/src/pythonai_grounded_generator.egg-info/dependency_links.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pythonai_grounded
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Unit tests using fake tokenizer and model: no model download needed."""
|
|
2
|
+
|
|
3
|
+
from types import SimpleNamespace
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
import torch
|
|
7
|
+
|
|
8
|
+
from pythonai_grounded import generate_answer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TensorInputs(dict):
|
|
12
|
+
def to(self, device):
|
|
13
|
+
return self
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DummyTokenizer:
|
|
17
|
+
eos_token_id = 2
|
|
18
|
+
|
|
19
|
+
def __init__(self, final_length=8):
|
|
20
|
+
self.final_length = final_length
|
|
21
|
+
self.last_context_ids = None
|
|
22
|
+
self.last_messages = None
|
|
23
|
+
|
|
24
|
+
def apply_chat_template(self, messages, tokenize, **kwargs):
|
|
25
|
+
self.last_messages = [dict(message) for message in messages]
|
|
26
|
+
assert kwargs["enable_thinking"] is False
|
|
27
|
+
if tokenize:
|
|
28
|
+
return [1, 2, 3, 4]
|
|
29
|
+
return "\n".join(message["content"] for message in messages)
|
|
30
|
+
|
|
31
|
+
def __call__(self, text, **kwargs):
|
|
32
|
+
if kwargs.get("return_tensors") == "pt":
|
|
33
|
+
length = self.final_length
|
|
34
|
+
return TensorInputs({
|
|
35
|
+
"input_ids": torch.ones((1, length), dtype=torch.long),
|
|
36
|
+
"attention_mask": torch.ones((1, length), dtype=torch.long),
|
|
37
|
+
})
|
|
38
|
+
max_length = kwargs["max_length"]
|
|
39
|
+
self.last_context_ids = list(range(min(len(text.split()), max_length)))
|
|
40
|
+
return {"input_ids": self.last_context_ids}
|
|
41
|
+
|
|
42
|
+
def decode(self, ids, **kwargs):
|
|
43
|
+
ids = ids.tolist() if hasattr(ids, "tolist") else ids
|
|
44
|
+
return "GENERATED ANSWER" if 999 in ids else "context"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class DummyModel:
|
|
48
|
+
def __init__(self, limit=200):
|
|
49
|
+
self.config = SimpleNamespace(max_position_embeddings=limit)
|
|
50
|
+
self.last_inputs = None
|
|
51
|
+
|
|
52
|
+
def generate(self, **kwargs):
|
|
53
|
+
self.last_inputs = kwargs
|
|
54
|
+
return torch.cat(
|
|
55
|
+
[kwargs["input_ids"], torch.tensor([[999]], dtype=torch.long)],
|
|
56
|
+
dim=1,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_basic_generation_and_prompt():
|
|
61
|
+
model = DummyModel()
|
|
62
|
+
tokenizer = DummyTokenizer()
|
|
63
|
+
output = generate_answer(model, tokenizer, "What?", "evidence here", "Use evidence")
|
|
64
|
+
assert output == "GENERATED ANSWER"
|
|
65
|
+
assert tokenizer.last_messages[0]["content"] == "Use evidence"
|
|
66
|
+
assert "Question:\nWhat?" in tokenizer.last_messages[1]["content"]
|
|
67
|
+
assert model.last_inputs["do_sample"] is False
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_context_is_truncated():
|
|
71
|
+
model = DummyModel(limit=50)
|
|
72
|
+
tokenizer = DummyTokenizer()
|
|
73
|
+
generate_answer(model, tokenizer, "Question?", "word " * 100, "Instruction", 5)
|
|
74
|
+
assert len(tokenizer.last_context_ids) == 9 # 50 - 4 - 5 - 32
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.mark.parametrize("question,prompt,tokens", [
|
|
78
|
+
("", "Instruction", 64),
|
|
79
|
+
("Question", "", 64),
|
|
80
|
+
("Question", "Instruction", 0),
|
|
81
|
+
])
|
|
82
|
+
def test_invalid_inputs(question, prompt, tokens):
|
|
83
|
+
with pytest.raises(ValueError):
|
|
84
|
+
generate_answer(DummyModel(), DummyTokenizer(), question, "context", prompt, tokens)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_insufficient_budget():
|
|
88
|
+
with pytest.raises(ValueError, match="Not enough token budget"):
|
|
89
|
+
generate_answer(DummyModel(limit=30), DummyTokenizer(), "Q", "text", "S")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_final_length_check():
|
|
93
|
+
tokenizer = DummyTokenizer(final_length=100)
|
|
94
|
+
with pytest.raises(ValueError, match="exceeds"):
|
|
95
|
+
generate_answer(DummyModel(limit=90), tokenizer, "Q", "text", "S", 5)
|