Skip to content

Adding document classification pipeline - #1

Open
Hellebore wants to merge 3 commits into
base-sha/d5d9929347696697f84995ee21a63c09b14ea813from
head-sha/263c938c50df00aaf891fc633e68a8d124016afe/2025-05-02T11-41-41/8eb518
Open

Adding document classification pipeline#1
Hellebore wants to merge 3 commits into
base-sha/d5d9929347696697f84995ee21a63c09b14ea813from
head-sha/263c938c50df00aaf891fc633e68a8d124016afe/2025-05-02T11-41-41/8eb518

Conversation

@Hellebore

@Hellebore Hellebore commented May 2, 2025

Copy link
Copy Markdown

Greptile Summary

Adds a document classification pipeline that integrates with VertexAI to filter sensitive financial documents, implementing JWT authentication and structured prompts for accurate document categorization.

  • Added /pipelines/document_classifier_pipeline.py with VertexAI integration using TheFinAI/Fino1-8B model
  • Added /pipelines/document_classifier_pipeline/valves.json with three document categories (Financial Statements, Board Meeting Documents, Customer Communication Documents)
  • Implemented robust error handling for missing environment variables, JSON parsing failures, and API errors
  • Added JWT-based authentication flow for secure VertexAI API access
  • Structured system prompts to ensure clean JSON output format from the model

Summary by Sourcery

Add a document classification pipeline using VertexAI to filter sensitive financial documents based on their content

New Features:

  • Implement document classification pipeline with VertexAI integration
  • Add support for categorizing documents into financial, board meeting, and customer communication types

Enhancements:

  • Integrate JWT-based authentication for secure VertexAI API access
  • Implement robust error handling for API and environment variable issues

Chores:

  • Update main.py to include document classifier in available pipelines

@Hellebore

Copy link
Copy Markdown
Author

This is a benchmark review for experiment bakeoff.
Run ID: bakeoff/benchmark_2025-05-02T11-38-04_v1-36-0-dirty.

This pull request was cloned from https://github.com/websentry-ai/pipelines/pull/25. (Note: the URL is not a link to avoid triggering a notification on the original pull request.)

Experiment configuration
review_config:
  # User configuration for the review
  # - benchmark - use the user config from the benchmark reviews
  # - <value> - use the value directly
  user_review_config:
    enable_ai_review: true
    enable_rule_comments: false

    enable_complexity_comments: benchmark
    enable_security_comments: benchmark
    enable_tests_comments: benchmark
    enable_comment_suggestions: benchmark

    enable_pull_request_summary: benchmark
    enable_review_guide: benchmark

    enable_approvals: false
    base_branches: [base-sha.*]

  ai_review_config:
    # The model responses to use for the experiment
    # - benchmark - use the model responses from the benchmark reviews
    # - llm - call the language model to generate responses
    model_responses:
      comments_model: benchmark
      comment_validation_model: benchmark
      comment_suggestion_model: benchmark
      complexity_model: benchmark
      security_model: benchmark
      tests_model: benchmark
      pull_request_summary_model: benchmark
      review_guide_model: benchmark
      overall_comments_model: benchmark

# The pull request dataset to run the experiment on
pull_request_dataset:
# CodeRabbit
- https://github.com/neerajkumar161/node-coveralls-integration/pull/5
- https://github.com/gunner95/vertx-rest/pull/1
- https://github.com/Altinn/altinn-access-management-frontend/pull/1427
- https://github.com/theMr17/github-notifier/pull/14
- https://github.com/bearycool11/AI_memory_Loops/pull/142

# Greptile
- https://github.com/gumloop/guMCP/pull/119
- https://github.com/autoblocksai/python-sdk/pull/335
- https://github.com/grepdemos/ImageSharp/pull/6
- https://github.com/grepdemos/server/pull/61
- https://github.com/websentry-ai/pipelines/pull/25

# Graphite
- https://github.com/KittyCAD/modeling-app/pull/6648
- https://github.com/KittyCAD/modeling-app/pull/6628
- https://github.com/Varedis-Org/AI-Test-Repo/pull/2
- https://github.com/deeep-network/bedrock/pull/198
- https://github.com/Metta-AI/metta/pull/277

# Copilot
- https://github.com/hmcts/rpx-xui-webapp/pull/4438
- https://github.com/ganchdev/quez/pull/104
- https://github.com/xbcsmith/ymlfxr/pull/13
- https://github.com/tinapayy/B-1N1T/pull/36
- https://github.com/coder/devcontainer-features/pull/6

# Questions to ask to label the review comments
review_comment_labels: []
# - label: correct
#   question: Is this comment correct?

# Benchmark reviews generated by running
#   python -m scripts.experiment benchmark <experiment_name>
benchmark_reviews: []

@Hellebore

Copy link
Copy Markdown
Author

Reviewer's Guide

This pull request introduces a new filter pipeline for document classification. It integrates with VertexAI by generating JWT access tokens from environment variables for authentication. The pipeline sends user input along with a structured system prompt (configured via Valves and valves.json) to the VertexAI API, parses the JSON response to determine the document category, and blocks the request if the category matches configured filters.

File-Level Changes

Change Details Files
Added a new document classification pipeline.
  • Created the main pipeline logic within the Pipeline class.
  • Defined pipeline configuration (categories, VertexAI details) using Pydantic Valves.
  • Added default category configuration in valves.json.
  • Implemented the inlet method to process incoming requests and trigger classification.
  • Included startup logic (on_startup) to check environment variables and initialize the tokenizer.
pipelines/document_classifier_pipeline.py
pipelines/document_classifier_pipeline/valves.json
Implemented VertexAI integration and JWT authentication.
  • Added get_access_token function to generate JWT tokens using service account credentials from environment variables.
  • Implemented classify_document function to call the VertexAI API with the user message and a structured system prompt.
  • Included logic to parse the JSON response from VertexAI and handle potential errors.
pipelines/document_classifier_pipeline.py
Registered the new pipeline in the main application.
  • Added 'Document Classifier' entry to the pipelines_map dictionary.
main.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

@Hellebore Hellebore left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Hey @Hellebore - I've reviewed your changes - here's some feedback:

  • Configuration details are spread across environment variables, valves.json, and hardcoded elements; consider consolidating these sources.
  • Consider using the google-cloud-aiplatform client library instead of raw requests for VertexAI interactions.
Here's what I looked at during the review
  • 🟡 General issues: 3 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Documentation: all looks good

LangSmith trace

Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

self.valves = self.Valves()

# System prompt template (kept in code, not in valves)
self.system_prompt = """

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

suggestion: Consider using dedent to manage system prompt indentation.

Indentation adds unwanted whitespace; use textwrap.dedent to strip it and ensure clean prompts.

Suggested implementation:

import textwrap
        # System prompt template (kept in code, not in valves)
        self.system_prompt = textwrap.dedent("""
            You are a helpful assistant that categorizes finance document prompts into specific categories. 
            You should only output the JSON result without any other text or characters such as thinking or reasoning criterias.
            Determine the document type of the following user prompt by selecting the most relevant category from this list:
            {categories}

            Below is the explanation of each category:
            {explanation_categories}

            Please follow these instructions carefully:
            - Analyze the content and intent of the document based on the prompt.
            - Identify which single category best represents the document type.
            - Do not make assumptions beyond the information explicitly provided.

Comment on lines +230 to +240
print("prediction", prediction)
if isinstance(prediction, str):
# Try to parse the JSON response
if '</think>' in prediction:
import re
prediction = prediction.split('</think>')[1].lstrip('\n')
prediction = re.sub(r'```json\s*|\s*```', '', prediction).strip()

if '## Final Response' in prediction and prediction.endswith('}'):
prediction = prediction.split('## Final Response')[1].lstrip("\n")
prediction = prediction.strip()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

suggestion (bug_risk): Review the JSON extraction logic from the prediction text.

Relying on fixed markers like '' for splitting is fragile—use a more robust parser to handle varied outputs.

Suggested change
print("prediction", prediction)
if isinstance(prediction, str):
# Try to parse the JSON response
if '</think>' in prediction:
import re
prediction = prediction.split('</think>')[1].lstrip('\n')
prediction = re.sub(r'```json\s*|\s*```', '', prediction).strip()
if '## Final Response' in prediction and prediction.endswith('}'):
prediction = prediction.split('## Final Response')[1].lstrip("\n")
prediction = prediction.strip()
print("prediction", prediction)
if isinstance(prediction, str):
import re
# Extract the first JSON object from the response using regex
json_match = re.search(r'(\{.*\})', prediction, re.DOTALL)
if json_match:
prediction = json_match.group(1)
prediction = re.sub(r'```json\s*|\s*```', '', prediction).strip()
else:
# Optionally log an error or return a message if JSON extraction fails
return {"category": "No valid JSON found in classification result"}

}

# Make the API request
response = requests.post(url, headers=headers, json=payload)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

suggestion (performance): Use an asynchronous HTTP client to avoid blocking in async functions.

Because classify_document runs in an async context, use an async HTTP client (e.g., httpx) to avoid blocking the event loop.

Suggested implementation:

import httpx
            # Make the API request using an asynchronous HTTP client
            async with httpx.AsyncClient() as client:
                response = await client.post(url, headers=headers, json=payload)
            response.raise_for_status()

Ensure that the function containing these lines is defined as an async function (e.g., async def classify_document(...)). Also, update any call sites to use await when calling this function.

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.

3 participants