Skip to content

Commit 5ae6803

Browse files
authored
Improve SamplingParams (#1283)
This PR: 1) moves sampling_params out of interpreter 2) allows creating new model with updated sampling_params ```python llamacpp_lm = guidance.models.LlamaCpp( model="Llama-3.2-3B-Instruct-UD-Q8_K_XL.gguf", echo=False, n_ctx=8192, sampling_params=SamplingParams( top_p=0.95, top_k=64 ) ) lm = llamacpp_lm.with_sampling_params(SamplingParams(top_p=0.8, top_k=128)) ```
1 parent e631040 commit 5ae6803

11 files changed

Lines changed: 65 additions & 60 deletions

File tree

guidance/library/_gen.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def gen(
9696
raise ValueError("Cannot use `regex` with `tools`")
9797

9898
assert n == 1, "We still need to add support for n>1! Consider putting your gen call in a loop for now."
99-
assert top_p == 1, "We still need to add support for top_p != 1!"
99+
assert top_p == 1, "Please use `model.with_sampling_params` to set top_p."
100100

101101
logger.debug(f'start gen(name="{name}")')
102102

guidance/models/_base/_interpreter.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,8 @@
3131

3232

3333
class Interpreter(Generic[S]):
34-
def __init__(self, state: S, default_sampling_params: Optional[SamplingParams] = None):
34+
def __init__(self, state: S):
3535
self.state = state
36-
self.default_sampling_params = SamplingParams() if default_sampling_params is None else default_sampling_params
3736

3837
def run(self, node: ASTNode, **kwargs) -> Iterator[OutputAttr]:
3938
yield from node.simplify()._run(self, **kwargs)

guidance/models/_base/_model.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class Model:
5959
def __init__(
6060
self,
6161
interpreter: Interpreter[S],
62-
default_sampling_params: Optional[SamplingParams] = None,
62+
sampling_params: SamplingParams,
6363
echo: bool = True,
6464
) -> None:
6565
self.echo = echo
@@ -70,6 +70,7 @@ def __init__(
7070

7171
self._interpreter = interpreter
7272
self._active_blocks: dict[Block, int] = {}
73+
self.sampling_params: SamplingParams = sampling_params
7374

7475
self._parent: Optional["Model"] = None
7576
self._parent_id: Optional[int] = None
@@ -131,7 +132,8 @@ def _apply_node(self, node: ASTNode) -> Self:
131132
else:
132133
self._update_trace_node(self._id, self._parent_id, StatelessGuidanceInput(value=node))
133134

134-
for i, output_attr in enumerate(self._interpreter.run(node)):
135+
# NOTE: passing a copy of the sampling parameters to avoid modifying the original
136+
for i, output_attr in enumerate(self._interpreter.run(node, sampling_params=self.sampling_params.copy())):
135137
if i != 0:
136138
# On the first iteration, we already have a fresh trace node
137139
# TODO: should be allowed to associate multiple output_attrs with a single input node?
@@ -291,6 +293,12 @@ def log_prob(self, key: str, default: Optional[D] = None) -> Union[float, list[U
291293
else:
292294
return captures["log_prob"]
293295

296+
def with_sampling_params(self, sampling_params: SamplingParams) -> Self:
297+
"""Return a new model with the given sampling parameters set."""
298+
self = self.copy()
299+
self.sampling_params = sampling_params
300+
return self
301+
294302
def __getattribute__(self, name):
295303
if name == "engine":
296304
# For legacy model.engine access (mostly for tests...)

guidance/models/_engine/_interpreter.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414

1515

1616
class EngineInterpreter(Interpreter[EngineState]):
17-
def __init__(self, engine: Engine, default_sampling_params: Optional[SamplingParams] = None):
18-
super().__init__(state=EngineState(), default_sampling_params=default_sampling_params)
17+
def __init__(self, engine: Engine):
18+
super().__init__(state=EngineState())
1919
self.engine = engine
2020
self.chat_template = self.engine.get_chat_template()
2121

@@ -67,7 +67,7 @@ def grammar(self, node: GrammarNode, **kwargs) -> Iterator[OutputAttr]:
6767
grammar=node.ll_grammar(),
6868
ensure_bos_token=True,
6969
echo=False,
70-
sampling_params=self.default_sampling_params, # NOTE: passing default sampling params for now
70+
sampling_params=kwargs.pop("sampling_params", None),
7171
)
7272

7373
delayed_bytes = b""

guidance/models/_llama_cpp.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ def __init__(
230230
enable_backtrack=True,
231231
enable_ff_tokens=True,
232232
enable_monitoring=True,
233-
default_sampling_params: Optional[SamplingParams] = None,
233+
sampling_params: Optional[SamplingParams] = None,
234234
**llama_cpp_kwargs,
235235
):
236236
"""Build a new LlamaCpp model object that represents a model in a given state."""
@@ -244,5 +244,9 @@ def __init__(
244244
enable_monitoring=enable_monitoring,
245245
**llama_cpp_kwargs,
246246
)
247-
interpreter = EngineInterpreter(engine, default_sampling_params=default_sampling_params)
248-
super().__init__(interpreter=interpreter, echo=echo)
247+
interpreter = EngineInterpreter(engine)
248+
super().__init__(
249+
interpreter=interpreter,
250+
sampling_params=SamplingParams() if sampling_params is None else sampling_params,
251+
echo=echo,
252+
)

guidance/models/_mock.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ class Mock(Model):
160160
def __init__(
161161
self,
162162
byte_patterns=[],
163-
default_sampling_params: Optional[SamplingParams] = None,
163+
sampling_params: Optional[SamplingParams] = None,
164164
echo=False,
165165
force=False,
166166
**kwargs,
@@ -178,6 +178,7 @@ def __init__(
178178
super().__init__(
179179
interpreter=EngineInterpreter(engine),
180180
echo=echo,
181+
sampling_params=SamplingParams() if sampling_params is None else sampling_params,
181182
)
182183

183184

guidance/models/_openai.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ class OpenAIInterpreter(OpenAIRuleMixin, OpenAIJSONMixin, OpenAIRegexMixin, Base
1818
def __init__(
1919
self,
2020
model: str,
21-
default_sampling_params: Optional[SamplingParams],
2221
api_key: Optional[str] = None,
2322
**kwargs,
2423
):
@@ -30,16 +29,14 @@ def __init__(
3029
)
3130

3231
client = openai.OpenAI(api_key=api_key, **kwargs)
33-
super().__init__(
34-
model=model, client=OpenAIClientWrapper(client), default_sampling_params=default_sampling_params
35-
)
32+
super().__init__(model=model, client=OpenAIClientWrapper(client))
3633

3734

3835
class OpenAI(Model):
3936
def __init__(
4037
self,
4138
model: str,
42-
default_sampling_params: Optional[SamplingParams] = None,
39+
sampling_params: Optional[SamplingParams] = None,
4340
echo: bool = True,
4441
*,
4542
api_key: Optional[str] = None,
@@ -69,8 +66,7 @@ def __init__(
6966
interpreter_cls = OpenAIInterpreter
7067

7168
super().__init__(
72-
interpreter=interpreter_cls(
73-
model, api_key=api_key, default_sampling_params=default_sampling_params, **kwargs
74-
),
69+
interpreter=interpreter_cls(model, api_key=api_key, **kwargs),
70+
sampling_params=SamplingParams() if sampling_params is None else sampling_params,
7571
echo=echo,
7672
)

guidance/models/_openai_base.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -201,14 +201,8 @@ class BaseOpenAIInterpreter(Interpreter[OpenAIState]):
201201

202202
log_probs: bool = True
203203

204-
def __init__(
205-
self,
206-
model: str,
207-
client: BaseOpenAIClientWrapper,
208-
default_sampling_params: Optional[SamplingParams] = None,
209-
**kwargs,
210-
):
211-
super().__init__(state=OpenAIState(), default_sampling_params=default_sampling_params)
204+
def __init__(self, model: str, client: BaseOpenAIClientWrapper, **kwargs):
205+
super().__init__(state=OpenAIState())
212206
self.model = model
213207
self.client = client
214208

@@ -251,9 +245,14 @@ def _run(self, **kwargs) -> Iterator[OutputAttr]:
251245
f"OpenAI models do not support pre-filled assistant messages: got data {self.state.content}."
252246
)
253247

254-
# only process kwargs that are supported by the OpenAI API
255-
if "top_p" not in kwargs and "top_p" in self.default_sampling_params:
256-
kwargs["top_p"] = self.default_sampling_params["top_p"]
248+
sampling_params = kwargs.pop("sampling_params", None)
249+
if sampling_params:
250+
# only process kwargs that are supported by the OpenAI API
251+
if "top_p" not in kwargs:
252+
kwargs["top_p"] = sampling_params.get("top_p", None)
253+
254+
if sampling_params.get("top_k", None) is not None:
255+
raise ValueError("OpenAI models do not support top_k sampling.")
257256

258257
with self.client.streaming_chat_completions(
259258
model=self.model,
@@ -274,7 +273,8 @@ def _handle_stream(self, chunks: Iterator["ChatCompletionChunk"]) -> Iterator[Ou
274273
latency_ms = (t1 - t0) * 1000
275274
t0 = t1
276275

277-
if chunk.usage is not None:
276+
# NOTE: use getattr here as litellm does not return usage
277+
if getattr(chunk, "usage", None) is not None:
278278
# Update token usage
279279
usage.input_tokens += chunk.usage.prompt_tokens
280280
# Estimate forward passes as number of completion tokens

guidance/models/_transformers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ def __init__(
618618
enable_backtrack=True,
619619
enable_ff_tokens=True,
620620
enable_monitoring=True,
621-
default_sampling_params: Optional[SamplingParams] = None,
621+
sampling_params: Optional[SamplingParams] = None,
622622
**kwargs,
623623
):
624624
"""Build a new Transformers model object that represents a model in a given state."""
@@ -640,10 +640,10 @@ def __init__(
640640
enable_ff_tokens=enable_ff_tokens,
641641
enable_monitoring=enable_monitoring,
642642
**kwargs,
643-
),
644-
default_sampling_params=default_sampling_params,
643+
)
645644
)
646645
super().__init__(
647646
interpreter=client,
647+
sampling_params=SamplingParams() if sampling_params is None else sampling_params,
648648
echo=echo,
649649
)

guidance/models/experimental/_litellm.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def streaming_chat_completions(
6262
class LiteLLMInterpreter(BaseOpenAIInterpreter):
6363
SUPPORTED_ENDPOINT_TYPES = ["openai", "azure_ai", "azure", "gemini", "anthropic", "xai", "hosted_vllm"]
6464

65-
def __init__(self, model_description: dict, default_sampling_params: Optional[SamplingParams], **kwargs):
65+
def __init__(self, model_description: dict, **kwargs):
6666
try:
6767
import litellm
6868
except ImportError:
@@ -80,9 +80,7 @@ def __init__(self, model_description: dict, default_sampling_params: Optional[Sa
8080
# Otherwise, generation will fail for some endpoints.
8181
self.log_probs = False
8282

83-
super().__init__(
84-
model=self.model, client=self.client, default_sampling_params=default_sampling_params, **kwargs
85-
)
83+
super().__init__(model=self.model, client=self.client, **kwargs)
8684

8785
def _check_model(self, model_desc: dict) -> str:
8886
"""Check if the model description is valid."""
@@ -232,24 +230,23 @@ def _grammar_vllm(self, node: GrammarNode, **kwargs) -> Iterator[OutputAttr]:
232230
yield self.state.apply_capture(name=name, value=value, log_prob=log_probs, is_append=False)
233231

234232
def _process_kwargs(self, **kwargs):
235-
if "top_k" not in kwargs and "top_k" in self.default_sampling_params:
236-
kwargs["top_k"] = self.default_sampling_params["top_k"]
233+
sampling_params = kwargs.pop("sampling_params", None)
234+
if sampling_params is None:
235+
return kwargs
236+
237+
kwargs["top_p"] = sampling_params.pop("top_p", None)
238+
kwargs["top_k"] = sampling_params.pop("top_k", None)
237239

238240
return kwargs
239241

240242

241243
class LiteLLM(Model):
242244
def __init__(
243-
self,
244-
model_description: dict,
245-
default_sampling_params: Optional[SamplingParams] = None,
246-
echo: bool = True,
247-
**kwargs,
245+
self, model_description: dict, sampling_params: Optional[SamplingParams] = None, echo: bool = True, **kwargs
248246
):
249-
interpreter = LiteLLMInterpreter(
250-
model_description=model_description, default_sampling_params=default_sampling_params, **kwargs
251-
)
247+
interpreter = LiteLLMInterpreter(model_description=model_description, **kwargs)
252248
super().__init__(
253249
interpreter=interpreter,
250+
sampling_params=SamplingParams() if sampling_params is None else sampling_params,
254251
echo=echo,
255252
)

0 commit comments

Comments
 (0)