Skip to content
This repository was archived by the owner on Mar 5, 2026. It is now read-only.
This repository was archived by the owner on Mar 5, 2026. It is now read-only.

Call graph context significantly reduces token usage and improves explanation quality #59

Description

@aymannadeem

We tested how Claude explains methods in the Flask codebase, comparing standard context vs. Nuanced call graph context. Our results demonstrated significant improvements in token usage, cost, and response quality.

Using this issue to document specific experiment scenarios.

Experiment Setup

We selected the full_dispatch_request method from Flask's core request handling pipeline and asked Claude to explain it under two conditions:

  1. Without Nuanced: Claude searches and reads raw code
  2. With Nuanced: Claude was provided with the call graph generated by Nuanced

Key Results

Metric Without Nuanced With Nuanced Improvement
Token Usage/Cost $0.1579 $0.1025 35% reduction
Response Time 37.2s 43.1s Comparable
Explanation Quality Surface-level
Missing call relationships
Comprehensive pipeline explanation
Clear control flow understanding
Significantly better

Analysis

When using the Nuanced call graph, Claude:

  • Required 35% fewer tokens (direct cost savings)
  • Provided a more structured and complete explanation of the request handling pipeline
  • Clearly identified relationships between components
  • Understood the entire flow from preprocessing to error handling to response finalization

Without Nuanced, Claude's explanation was accurate but lacked depth in understanding how the components worked together, focusing more on summarizing the code than explaining its execution flow.

Raw Outputs

without Nuanced (click to expand)
> can you explain the full_dispatch_request method to me?

⏺ I'll find and examine the full_dispatch_request method in the Flask codebase.

⏺ Search(pattern: "full_dispatch_request", include: "*.py")…
⎿ Found 2 files

⏺ Let's look at the implementation in the main Flask application code:

⏺ Read(file_path: "src/flask/app.py")…
⎿ from future import annotations
import collections.abc as cabc
... (+1534 lines)

⏺ Let me look for the full_dispatch_request method which is defined around line 904:

The full_dispatch_request method in Flask is responsible for handling the complete HTTP request-response cycle. Here's what it does:

  1. It marks the app as having received its first request with self._got_first_request = True
  2. It wraps the entire request handling process in a try-except block to catch any exceptions
  3. Within the try block:
    - It sends the request_started signal
    - Calls preprocess_request() which runs URL value preprocessors and before-request handlers
    - If a before-request handler returned a value, it's used as the response
    - Otherwise, it calls dispatch_request() to find and execute the view function for the route
  4. If any exception occurs during preprocessing or dispatching, it's caught and passed to handle_user_exception()
  5. Finally, it calls finalize_request() on the response value, which:
    - Converts the view function's return value to a proper Response object
    - Processes the response through any after-request handlers
    - Sends the request_finished signal

This method is the main entry point for request handling in Flask and orchestrates the entire request pipeline, including error handling,
before/after request hooks, and view function execution.

With Nuanced (click to expand)
> can you explain the full_dispatch_method to me using its call graph: {
    "src.flask.app.Flask.full_dispatch_request": {
      "filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
      "callees": [
        "src.flask.app.Flask.preprocess_request",
        "src.flask.app.Flask.dispatch_request",
        "src.flask.app.Flask.handle_user_exception",
        "src.flask.app.Flask.finalize_request"
      ]
    },
    "src.flask.app.Flask.handle_user_exception": {
      "filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
      "callees": [
        "src.flask.app.Flask.handle_http_exception",
        "src.flask.app.Flask.ensure_sync",
        "<builtin>.isinstance"
      ]
    },
    "src.flask.app.Flask.preprocess_request": {
      "filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
      "callees": [
        "<builtin>.reversed",
        "src.flask.app.Flask.ensure_sync"
      ]
    },
    "src.flask.app.Flask.finalize_request": {
      "filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
      "callees": [
        "src.flask.app.Flask.process_response",
        "src.flask.app.Flask.make_response"
      ]
    },
    "src.flask.app.Flask.process_response": {
      "filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
      "callees": [
        "<builtin>.reversed",
        "src.flask.app.Flask.ensure_sync",
        "itertools.chain"
      ]
    },
    "src.flask.app.Flask.handle_http_exception": {
      "filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
      "callees": [
        "src.flask.app.Flask.ensure_sync",
        "<builtin>.isinstance"
      ]
    },
    "src.flask.app.Flask.dispatch_request": {
      "filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
      "callees": [
        "src.flask.app.Flask.make_default_options_response",
        "<builtin>.getattr",
        "src.flask.app.Flask.raise_routing_exception",
        "src.flask.app.Flask.ensure_sync"
      ]
    },
    "src.flask.app.Flask.make_response": {
      "filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
      "callees": [
        "<builtin>.isinstance",
        "<builtin>.type",
        "<builtin>.TypeError",
        "<builtin>.len",
        "<builtin>.callable"
      ]
    },
    "src.flask.app.Flask.ensure_sync": {
      "filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
      "callees": [
        "inspect.iscoroutinefunction",
        "src.flask.app.Flask.async_to_sync"
      ]
    },
    "src.flask.app.Flask.raise_routing_exception": {
      "filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
      "callees": [
        "debughelpers.FormDataRoutingRedirect",
        "<builtin>.isinstance"
      ]
    },
    "src.flask.app.Flask.async_to_sync": {
      "filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
      "callees": [
        "asgiref.sync.async_to_sync",
        "<builtin>.RuntimeError"
      ]
    },
    "src.flask.app.Flask.make_default_options_response": {
      "filepath": "/Users/aymannadeem/code/flask/src/flask/app.py",
      "callees": []
    }
  }

⏺ Let me examine the Flask.full_dispatch_request method and its call graph to explain how it processes requests.

⏺ Read(file_path: "src/flask/app.py")…
⎿ from future import annotations
import collections.abc as cabc
... (+1534 lines)

⏺ Based on the code examination, I can explain the full_dispatch_request method in Flask by following its call graph:

The Flask request handling pipeline centers around full_dispatch_request, which orchestrates the entire request-response cycle. Here's how
it works:

  1. full_dispatch_request (line 904-921):
    - Marks that the application has received its first request
    - Sends the request_started signal
    - Calls 4 main methods in sequence:

  2. First calls preprocess_request:
    - Processes URL values through registered preprocessors
    - Executes all registered before_request functions in reverse order
    - If any returns a non-None value, it skips to finalize_request directly

  3. If preprocessing completes with no early response, calls dispatch_request:
    - Checks for routing exceptions and raises them properly
    - Handles automatic OPTIONS requests if configured
    - Retrieves and calls the appropriate view function for the request

  4. If an exception occurs at any point, calls handle_user_exception:
    - Determines if the exception is an HTTP exception
    - For HTTP exceptions, calls handle_http_exception
    - For other exceptions, finds an appropriate error handler or re-raises

  5. Finally, calls finalize_request:
    - Converts the view function result to a Response using make_response
    - Processes the response with process_response, which:

    • Runs all registered after_request functions in reverse order
    • Saves any session data to the response
      - Sends the request_finished signal

The entire flow ensures proper request preprocessing, dispatching, error handling, and response finalization, maintaining Flask's
middleware-like functionality through its various hook points.

Conclusion

Providing AI coding assistants with structured call graph context rather than raw code delivers substantial benefits:

  1. Lower costs through reduced token usage
  2. Better understanding of code relationships and execution flow
  3. Higher quality explanations that focus on how code works, not just what it does

This experiment confirms our thesis that AI coding tools need rich, structured context rather than just more raw code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    evaluation scenarioFor tracking scoping, setup and execution of ad-hoc evaluation scenarios

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions