Skip to content

Use tool calling - #121

Merged
pilar12 merged 5 commits into
mainfrom
usee-tool-calling
Aug 12, 2026
Merged

pilar12 merged 5 commits into
mainfrom
usee-tool-calling

Conversation

@pilar12

@pilar12 pilar12 commented Aug 11, 2026 •

Copy link
Copy Markdown
Collaborator

Added option to use tool calling (now default) to extract data instead of the response format option.

tested with:

  • anthropic/claude-sonnet-5
  • anthropic/claude-opus-5
  • openrouter/deepseek/deepseek-v4-pro
  • openrouter/deepseek/deepseek-chat
  • openrouter/z-ai/glm-5.2
  • openrouter/moonshotai/kimi-k3
  • openai/gpt-5.6-luna
  • openai/gpt-5.6-sol
  • openai/gpt-5.6-terra
    reasoning effort needs to be explicitly set to 'none' for the gpt-5.6 models so by default am setting it to 'none' when using tool_calling

with openrouter/gpt-oss-120b it fails as the response contains only the reasoning content and nothing in the message.content or message.tool_calls

Summary by Sourcery

Switch the extraction flow to use tool-based schema calling by default while keeping response_format as an optional alternative, and update related configuration and models.

New Features:

  • Introduce a configurable extraction_method option supporting both response_format and tool_call approaches.
  • Add support for a unit field on AdditionalParameter in the reduced pydantic model schema.

Enhancements:

  • Use tool calling with a formatted Pydantic schema for data extraction, including automatic tool selection and reasoning configuration.
  • Propagate extraction_method through the pipeline so callers can choose the extraction strategy per run.
  • Simplify failure handling by storing the raw assistant message object when postprocessing fails.

Build:

  • Add EXTRACTION_METHODS Literal type to central configuration for consistent extraction method specification.

Summary by CodeRabbit

  • New Features
    • Added support for selecting between tool-based and response-format extraction methods.
    • Added an optional unit field to additional extraction parameters.
    • Improved handling of structured extraction responses and validation retries.
  • Bug Fixes
    • Improved failure handling by preserving raw model responses when postprocessing fails.

Copilot AI lite review requested due to automatic review settings August 11, 2026 07:48
@sourcery-ai

sourcery-ai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Switches the default extraction pathway from response_format to tool calling and plumbs an extraction_method option through configuration, LLM call, and pipeline; adds schema formatting for tools, adjusts failure handling, and extends the Pydantic schema slightly.

Sequence diagram for the new extraction_method tool_call vs response_format flow

sequenceDiagram
    participant Pipeline as PerlaExtractPipeline
    participant LLM as llm_call
    participant LiteLLM as litellm_completion
    participant Model as PerovskiteSolarCells

    Pipeline->>LLM: create_text_completion(model_name, pdf_text, extraction_method)
    LLM->>LLM: _create_text_completion(extraction_method)
    LLM->>LLM: format_schema(PerovskiteSolarCells)

    alt extraction_method == response_format
        LLM->>LiteLLM: completion(response_format=PerovskiteSolarCells)
        LiteLLM-->>LLM: resp
        LLM->>Model: model_validate_json(resp.choices[0].message.content)
    else extraction_method == tool_call
        LLM->>LiteLLM: completion(tools, tool_choice, reasoning_effort='none')
        LiteLLM-->>LLM: resp
        LLM->>Model: model_validate_json(resp.choices[0].message.tool_calls[0].function.arguments)
    end

    LLM-->>Pipeline: extracted_data, resp
    Pipeline->>Pipeline: postprocess(extracted_data.model_dump())
Loading

File-Level Changes

Change Details Files
Allow choosing between response_format and tool_call extraction, with tool_call as the new default, and wire this through the LLM call path.
  • Add EXTRACTION_METHODS Literal type and default extraction_method parameter to create_text_completion and _create_text_completion
  • Remove inline schema from user message content and rely on tools/response_format instead
  • Build filtered_params dynamically, attaching either response_format or tools/tool_choice plus reasoning_effort='none' based on extraction_method
  • Select data source from either message.content or message.tool_calls[0].function.arguments and pass it into PerovskiteSolarCells.model_validate_json
  • Adjust retry message history to store the raw JSON/tool arguments string used for validation
src/perla_extract/llm_call.py
src/perla_extract/configuration.py
Introduce a schema formatter for tool definitions and use it to define a function tool for extraction.
  • Implement format_schema helper using Pydantic model_json_schema to build a tools-compatible function schema
  • Strip title/description and normalize required fields in the generated parameters
  • Use formatted schema to construct tools list and tool_choice for completion calls
src/perla_extract/llm_call.py
Propagate extraction_method configuration through the pipeline and simplify failure handling output.
  • Add extraction_method parameter to ExtractionPipeline.init and store on self
  • Pass extraction_method through extract() CLI helper into the pipeline and down to llm_call.create_text_completion
  • On failure, fall back to serializing the raw message object instead of trying to json.loads message.content
src/perla_extract/pipeline.py
Extend the Pydantic schema slightly to capture units on additional parameters and clean up a commented field.
  • Add unit field to AdditionalParameter model
  • Fix indentation / comment style for the alternative additional_parameters field in ProcessingStep
src/perla_extract/pydantic_model_reduced.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@pilar12, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 44 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 55791454-51d2-41d6-b954-72816c0fb686

📥 Commits

Reviewing files that changed from the base of the PR and between 69d2e18 and fae041c.

📒 Files selected for processing (3)
  • src/perla_extract/llm_call.py
  • src/perla_extract/pipeline.py
  • src/perla_extract/pydantic_model_reduced.py
📝 Walkthrough

Walkthrough

The PR adds typed support for "response_format" and "tool_call" extraction methods. It formats tool schemas, parses both response types, propagates the method through the pipeline, and adds an optional unit field.

Changes

Extraction method support

Layer / File(s) Summary
Extraction contracts and output fields
src/perla_extract/configuration.py, src/perla_extract/pydantic_model_reduced.py
Defines the supported extraction methods. Adds an optional unit field to AdditionalParameter.
Completion request and response handling
src/perla_extract/llm_call.py
Builds response-format or required tool-call requests. Parses the selected output format and validates the extracted data.
Pipeline method propagation and failure output
src/perla_extract/pipeline.py
Accepts and propagates extraction_method. Postprocesses model dumps and stores stringified messages when postprocessing fails.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant extract
  participant ExtractionPipeline
  participant create_text_completion
  participant LiteLLM
  extract->>ExtractionPipeline: extraction_method
  ExtractionPipeline->>create_text_completion: configured extraction_method
  create_text_completion->>LiteLLM: response_format or tool-call request
  LiteLLM-->>create_text_completion: model response
  create_text_completion-->>ExtractionPipeline: validated extraction result
Loading

Possibly related PRs

Suggested reviewers: sherjeelshabih

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding tool-calling support for extraction.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch usee-tool-calling

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • When using tool-based extraction you assume resp.choices[0].message.tool_calls[0].function.arguments is always present; consider adding defensive checks (or a model-capability fallback to response_format) to avoid crashes on models that don’t return tool calls.
  • The hardcoded reasoning_effort = 'none' for all tool-calling requests is described as needed only for gpt-5.6 models; you might want to gate this by model_name or make it configurable so other models can still use their default reasoning settings.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- When using tool-based extraction you assume `resp.choices[0].message.tool_calls[0].function.arguments` is always present; consider adding defensive checks (or a model-capability fallback to `response_format`) to avoid crashes on models that don’t return tool calls.
- The hardcoded `reasoning_effort = 'none'` for all tool-calling requests is described as needed only for gpt-5.6 models; you might want to gate this by `model_name` or make it configurable so other models can still use their default reasoning settings.

## Individual Comments

### Comment 1
<location path="src/perla_extract/llm_call.py" line_range="67-69" />
<code_context>
     """Custom exception to indicate that the maximum number of retries has been exceeded."""
     pass

+def format_schema(model: BaseModel) -> dict:
+    #adaptede from https://github.com/567-labs/instructor/blob/47fdb2ca07119d389a3c0e8bc28b9930b814f294/instructor/v2/providers/openai/schema.py
+    schema = model.model_json_schema()
+    parameters = {k: v for k, v in schema.items() if k not in ("title", "description")}
</code_context>
<issue_to_address>
**nitpick (typo):** Fix minor typo in the comment referencing the source implementation.

In the inline comment, change `adaptede` to `adapted` to correct the typo in the attribution.

```suggestion
def format_schema(model: BaseModel) -> dict:
    #adapted from https://github.com/567-labs/instructor/blob/47fdb2ca07119d389a3c0e8bc28b9930b814f294/instructor/v2/providers/openai/schema.py
    schema = model.model_json_schema()
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/perla_extract/llm_call.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/perla_extract/pipeline.py (1)

323-338: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Forward extraction configuration in the Nomad path.

extract_from_pdf_nomad calls create_text_completion at lines 348-350 without self.extraction_method or self.additional_params. A Nomad extraction configured for "response_format" silently uses the "tool_call" default.

Proposed fix
         results, completion_usage = llm_call.create_text_completion(
-            self.model_name, pdf_text, api_key=api_key, api_base_url=api_base_url
+            self.model_name,
+            pdf_text,
+            api_key=api_key,
+            api_base_url=api_base_url,
+            extraction_method=self.extraction_method,
+            additional_params=self.additional_params,
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/perla_extract/pipeline.py` around lines 323 - 338, Update the Nomad
extraction flow in extract_from_pdf_nomad so its create_text_completion call
forwards self.extraction_method and self.additional_params. Preserve the
configured values, including response_format, instead of allowing
create_text_completion to fall back to the tool_call default.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/perla_extract/llm_call.py`:
- Line 219: Update the tool-call request parameter handling around
filtered_params to use setdefault for reasoning_effort, retaining "none" as the
default while preserving any explicit value supplied through additional_params.
- Around line 233-237: Update the non-response_format branch in the LLM
extraction flow to safely handle missing or empty message.tool_calls before
indexing the first call or accessing its function arguments. Normalize absent
tool-call data into the exception type handled by the existing validation retry
logic, while preserving normal argument extraction for valid tool-call
responses.

In `@src/perla_extract/pipeline.py`:
- Around line 323-324: Update the affected constructor signature so existing
positional callers still bind their eighth argument to additional_params: place
extraction_method after additional_params, or make extraction_method
keyword-only. Preserve the current defaults and support the repository’s
existing named-argument usage.

---

Outside diff comments:
In `@src/perla_extract/pipeline.py`:
- Around line 323-338: Update the Nomad extraction flow in
extract_from_pdf_nomad so its create_text_completion call forwards
self.extraction_method and self.additional_params. Preserve the configured
values, including response_format, instead of allowing create_text_completion to
fall back to the tool_call default.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 46721b4c-7b73-44fd-bb8e-e09ae273126e

📥 Commits

Reviewing files that changed from the base of the PR and between e48f2ff and 69d2e18.

📒 Files selected for processing (4)
  • src/perla_extract/configuration.py
  • src/perla_extract/llm_call.py
  • src/perla_extract/pipeline.py
  • src/perla_extract/pydantic_model_reduced.py

Comment thread src/perla_extract/llm_call.py Outdated
Comment thread src/perla_extract/llm_call.py
Comment thread src/perla_extract/pipeline.py

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR changes the extraction pipeline to use tool-calling (function/tool schema) by default instead of response_format, while still supporting response_format as an alternative. It also propagates an extraction_method option through the pipeline and extends the reduced Pydantic schema with a unit field on AdditionalParameter.

Changes:

  • Add EXTRACTION_METHODS and thread a new extraction_method option through pipeline.py → llm_call.py.
  • Implement tool-calling schema formatting + forced tool selection for structured extraction.
  • Extend AdditionalParameter with a unit field.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
src/perla_extract/pydantic_model_reduced.py Adds AdditionalParameter.unit and adjusts nearby schema/comment formatting.
src/perla_extract/pipeline.py Propagates extraction_method into LLM calls and simplifies failure raw-output capture.
src/perla_extract/llm_call.py Adds tool-calling path (schema formatting + tool choice) and routes validation to tool-call arguments.
src/perla_extract/configuration.py Introduces EXTRACTION_METHODS Literal for consistent method typing.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/perla_extract/llm_call.py
Comment thread src/perla_extract/llm_call.py Outdated
Comment thread src/perla_extract/pipeline.py Outdated
Comment thread src/perla_extract/pydantic_model_reduced.py Outdated
Comment thread src/perla_extract/llm_call.py Outdated
@pilar12
pilar12 requested a review from sherjeelshabih August 11, 2026 08:06
{
"role": "user",
"content": f"{system_prompt}\n{instruction}\n Here is the schema: {PerovskiteSolarCells.model_json_schema()!s} \n\nHere is the text:\n{pdf_text}",
"content": f"{system_prompt}\n{instruction} \n\nHere is the text:\n{pdf_text}",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you ablate that it is generally better not to show the schema?

I would think so, but did you test?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The evals that i ran last week were with the schema removed from the prompt. I would say still not conclusive.

@kjappelbaum kjappelbaum left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes generally sense to me. I only had one question (also for understanding how this would impact our old results)

@pilar12
pilar12 merged commit c9d34bc into main Aug 12, 2026
11 checks passed
@pilar12
pilar12 deleted the usee-tool-calling branch August 17, 2026 11:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants