Skip to content

Commit 5548efd

Browse files
rmitschRaphael Mitsch
andauthored
Run async batching in Outlines engine when native batching not available (#170)
* feat: Use async batching for models without batch support in outlines. * feat: Use async batching for models without batch support in outlines. * fix: Expose OPENROUTER_API_KEY. * test: Remove redundant test. --------- Co-authored-by: Raphael Mitsch <raphael@climatiq.com>
1 parent 15c8e83 commit 5548efd

4 files changed

Lines changed: 44 additions & 30 deletions

File tree

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ jobs:
9797
- name: Run tests
9898
env:
9999
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
100+
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
100101
run: |
101102
source .venv/bin/activate
102103
pytest -x --cov --cov-report=xml -m "not slow"

sieves/engines/outlines_.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,17 @@ def generate(prompts: list[str]) -> Iterable[Result]:
7373
results = generator.batch(prompts, **self._inference_kwargs)
7474
# Batch mode is not implemented for all Outlines wrappers. Fall back to single-prompt mode in that case.
7575
except NotImplementedError:
76-
results = [generator(prompt, **self._inference_kwargs) for prompt in prompts]
76+
77+
async def generate_async(prompt: str) -> Result | None:
78+
"""Generate result async.
79+
80+
:param prompt: Prompt to generate result for.
81+
:return: Result for prompt. Results are None if corresponding prompt failed.
82+
"""
83+
return generator(prompt, **self._inference_kwargs)
84+
85+
calls = [generate_async(prompt) for prompt in prompts]
86+
results = self._execute_async_calls(calls)
7787

7888
if inference_mode == InferenceMode.json:
7989
assert len(results) == len(prompts)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Test engine-related edge cases, e.g. specific model-task combinations or engine behavior under certain conditions."""
2+
import os
3+
4+
import openai
5+
import outlines
6+
7+
from sieves import tasks, GenerationSettings, Pipeline, Doc
8+
9+
10+
def test_openai_outlines_batching() -> None:
11+
"""Test Outlines batching fallback uses async batching."""
12+
model = "gpt-5-nano"
13+
client = openai.OpenAI(
14+
base_url="https://openrouter.ai/api/v1",
15+
api_key=os.environ["OPENROUTER_API_KEY"],
16+
)
17+
model = outlines.from_openai(client, model_name=model)
18+
19+
classifier = tasks.Classification(
20+
task_id="procedures_classifier",
21+
labels=["Fruit", "Vegetable"],
22+
model=model,
23+
generation_settings=GenerationSettings(
24+
batch_size=10,
25+
strict_mode=False,
26+
inference_kwargs={"max_tokens": 200},
27+
),
28+
multi_label=True,
29+
)
30+
31+
docs = [Doc(text="Apple"), Doc(text="Carrot"), Doc(text="Watermelon")]
32+
list(Pipeline(classifier)(docs))

sieves/tests/tasks/postprocessing/test_distillation.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -199,32 +199,3 @@ def test_serialization(classification_docs, batch_runtime) -> None:
199199

200200

201201
Pipeline.deserialize(config=config, tasks_kwargs=[{"model": batch_runtime.model}, {}])
202-
203-
204-
@pytest.mark.skip(reason="No OpenAI API key available in GitHub CI yet")
205-
def test_distillation_with_openai_model() -> None:
206-
"""Test distillation with an OpenAI model.
207-
208-
See https://github.com/MantisAI/sieves/issues/162.
209-
"""
210-
client = openai.OpenAI(api_key=os.environ['OPENAI_API_KEY'])
211-
model = outlines.from_openai(client=client, model_name='gpt-5-nano')
212-
213-
classifier = Classification(
214-
task_id='classifier',
215-
labels=['Fruit', 'Vegetable'],
216-
model=model,
217-
generation_settings=GenerationSettings(batch_size=32, strict_mode=False),
218-
)
219-
220-
distiller = Distillation(
221-
target_task_id='classifier',
222-
base_model_id='sentence-transformers/paraphrase-mpnet-base-v2',
223-
framework=DistillationFramework.setfit,
224-
output_path='./model',
225-
train_frac=.5,
226-
val_frac=.5,
227-
)
228-
229-
pipe = classifier + distiller
230-
list(pipe([Doc(text='Apple'), Doc(text='Cucumber'), Doc(text="Broccoli"), Doc(text='Pomegranate')]))

0 commit comments

Comments
 (0)