Skip to content

interlens.participant.participants.gemma

interlens.participant.participants.gemma

GemmaModelParticipant dataclass

GemmaModelParticipant(
    name: str = "",
    hf_id: str | None = None,
    weights_path: str | None = None,
    dtype: str = "bfloat16",
    attn: str = "flash_attention_2",
    quant: str | None = None,
    revision: str | None = None,
    device: str | device | None = None,
    max_new_tokens: int = 512,
    temperature: float = 0.8,
    top_p: float = 0.95,
    seed: int | None = None,
    thinking: bool | str = "auto",
    system_prompt: str | None = None,
    private_context: tuple = (),
    tools: tuple = (),
    max_tool_iters: int = 4,
    kv_reuse: bool | str = "auto",
    steering: object = None,
    _model: "PreTrainedModel | None" = None,
    _tokenizer: "PreTrainedTokenizerBase | None" = None,
)

Bases: ModelParticipant

A Gemma-family participant. The only thing that differs from base is the tool-call format (```tool_code); the chat-template flags (Gemma 2 rejects a standalone system role and requires strict user/model alternation, Gemma 3 accepts a system role) are auto-derived from the tokenizer's own template, so both generations use this one class.

parse_tool_calls

parse_tool_calls(text: str) -> list

Parse Gemma's ```tool_code function-call blocks (best-effort).

            Gemma emits calls like ```` ```tool_code

name(arg="x", n=2) ` ```` rather than Hermes JSON. We extract the block, then the function name and simple keyword arguments. Falls back to[]`` (treat as final message) on anything it can't parse, matching the base contract of never misfiring silently.

Source code in src/interlens/participant/participants/gemma.py
def parse_tool_calls(self, text: str) -> list:
	"""Parse Gemma's ```` ```tool_code ```` function-call blocks (best-effort).

	Gemma emits calls like ```` ```tool_code\nname(arg="x", n=2)\n``` ```` rather than Hermes JSON. We extract
	the block, then the function name and simple keyword arguments. Falls back to ``[]`` (treat as final
	message) on anything it can't parse, matching the base contract of never misfiring silently.
	"""
	import ast
	import re
	from ...tools.tool_call import ToolCall

	calls = []
	for block in re.findall(r"```tool_code\s*(.*?)```", text, re.DOTALL):
		call_match = re.match(r"\s*([A-Za-z_][\w.]*)\s*\((.*)\)\s*$", block.strip(), re.DOTALL)
		if not call_match:
			continue
		name, arg_src = call_match.group(1), call_match.group(2)
		arguments = {}
		try:
			# Parse ``k=v`` pairs via a synthetic call node, so values are real Python literals.
			parsed = ast.parse(f"f({arg_src})", mode="eval")
			for kw in parsed.body.keywords:
				arguments[kw.arg] = ast.literal_eval(kw.value)
		except (SyntaxError, ValueError) as exc:
			logger.debug("dropping unparseable tool_code block %r: %s", block, exc)
			continue
		calls.append(ToolCall(name=name.split(".")[-1], arguments=arguments, raw=block))
	return calls