This report presents the forensic synthetic code analysis of langchain-ai/open_deep_research, a Python project with 12,481 GitHub stars. SynthScan v2.0 examined 8,675 lines of code across 36 source files, recording 177 pattern matches distributed across 14 syntactic categories. The overall adjusted score of 26.4 places this repository in the Moderate AI signal band.
The scanner applied 160+ deterministic lexical heuristics, multi-line block detectors, abstract syntax tree depth profilers, and a cross-file Jaccard similarity matrix to construct a statistically normalised synthetic code estimate. All matches are individually weighted by severity coefficient and contextual multiplier before summation, and the resulting headline score is temporally discounted to account for the repository's development history relative to the commercial emergence of large language model coding tooling (November 2022 onward).
Longitudinal tracking requires multiple scan runs. Once this repository is re-scanned after new commits land, this chart will visualise how the synthetic code signal evolves over time — enabling you to detect whether AI authorship is growing, stabilising, or being actively corrected by human engineers.
Classifies detected patterns by their diagnostic confidence and structural impact. CRITICAL patterns (coefficient 10) represent definitive synthetic signatures — hallucinated imports, explicit LLM attribution metadata — virtually never produced by human authors. HIGH (5) indicates strong structural tells such as cross-file repetition or cross-linguistic idioms. MEDIUM (2) covers recognisable conversational padding and AI-specific vocabulary. LOW (1) captures subtle indicators like tautological comments and generic boilerplate that require density to carry independent signal.
This horizontal bar chart decomposes the repository's raw synthetic code score by top-level directory, allowing you to pinpoint precisely which modules or components carry the highest AI authorship density. Directories with disproportionately high scores relative to their size warrant targeted manual review: concentrated AI signatures often trace back to mass-generated configuration layers, auto-ported test suites, LLM-scaffolded boilerplate classes, or entire subsystems authored under heavy copilot assistance. Use this view to prioritise your human code-review effort.
The scanner identified 177 distinct pattern matches across 14 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a statistically significant AI authorship indicator. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the diagnostic confidence level (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — comment-context matches receive a ×1.5 weight because LLMs systematically over-annotate. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier as dense clusters constitute far stronger evidence of synthetic authorship than isolated hits.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | src/open_deep_research/utils.py | 61 | # Step 1: Execute search queries asynchronously | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 70 | # Step 2: Deduplicate results by URL to avoid processing the same content multiple times | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 78 | # Step 3: Set up the summarization model with configuration | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 109 | # Step 5: Execute all summarization tasks in parallel | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 112 | # Step 6: Combine results with their summaries | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 464 | # Step 1: Handle authentication if required | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 470 | # Step 2: Validate configuration requirements | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 498 | # Step 4: Load tools from MCP server | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 506 | # Step 5: Filter and configure tools | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 688 | # Step 2: Check provider-specific token limit patterns | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 696 | # Step 3: If provider unknown, check all providers | COMMENT |
| LOW | src/open_deep_research/utils.py | 95 | # Step 4: Create summarization tasks (skip empty content) | COMMENT |
| LOW | src/open_deep_research/utils.py | 125 | # Step 7: Format the final output | COMMENT |
| LOW | src/open_deep_research/utils.py | 481 | # Step 3: Set up MCP server connection | COMMENT |
| LOW | src/open_deep_research/utils.py | 677 | # Step 1: Determine provider from model name if available | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 73 | # Step 1: Check if clarification is enabled in configuration | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 79 | # Step 2: Prepare the model for structured clarification analysis | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 96 | # Step 3: Analyze whether clarification is needed | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 103 | # Step 4: Route based on clarification analysis | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 149 | # Step 2: Generate structured research brief from user messages | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 156 | # Step 3: Initialize supervisor with research brief and instructions | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 212 | # Step 2: Generate supervisor response based on current context | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 216 | # Step 3: Update state and proceed to tool execution | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 413 | # Step 3: Generate researcher response with system context | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 417 | # Step 4: Update state and proceed to tool execution | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 525 | # Step 1: Configure the compression model | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 534 | # Step 2: Prepare messages for compression | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 540 | # Step 3: Attempt compression with retry logic for token limit issues | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 620 | # Step 1: Extract research findings and prepare state cleanup | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 625 | # Step 2: Configure the final report generation model | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 634 | # Step 3: Attempt report generation with token limit retry logic | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 132 | # Step 1: Set up the research model for structured output | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 192 | # Step 1: Configure the supervisor model with available tools | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 240 | # Step 1: Extract current state and check exit conditions | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 264 | # Step 2: Process all tool calls together (both think_tool and ConductResearch) | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 344 | # Step 3: Return command with all tool results | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 379 | # Step 1: Load configuration and validate tool availability | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 391 | # Step 2: Configure the researcher model with tools | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 451 | # Step 1: Extract current state and check early exit conditions | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 466 | # Step 2: Handle other tool calls (search, MCP tools, etc.) | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 491 | # Step 3: Check late exit conditions (after processing tools) | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 576 | # Step 4: Return error result if all attempts failed | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 692 | # Step 4: Return failure result if all retries exhausted | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | src/open_deep_research/utils.py | 61 | # Step 1: Execute search queries asynchronously | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 70 | # Step 2: Deduplicate results by URL to avoid processing the same content multiple times | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 78 | # Step 3: Set up the summarization model with configuration | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 109 | # Step 5: Execute all summarization tasks in parallel | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 112 | # Step 6: Combine results with their summaries | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 464 | # Step 1: Handle authentication if required | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 470 | # Step 2: Validate configuration requirements | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 498 | # Step 4: Load tools from MCP server | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 506 | # Step 5: Filter and configure tools | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 688 | # Step 2: Check provider-specific token limit patterns | COMMENT |
| LOW⚡ | src/open_deep_research/utils.py | 696 | # Step 3: If provider unknown, check all providers | COMMENT |
| LOW | src/open_deep_research/utils.py | 95 | # Step 4: Create summarization tasks (skip empty content) | COMMENT |
| LOW | src/open_deep_research/utils.py | 125 | # Step 7: Format the final output | COMMENT |
| LOW | src/open_deep_research/utils.py | 481 | # Step 3: Set up MCP server connection | COMMENT |
| LOW | src/open_deep_research/utils.py | 677 | # Step 1: Determine provider from model name if available | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 73 | # Step 1: Check if clarification is enabled in configuration | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 79 | # Step 2: Prepare the model for structured clarification analysis | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 96 | # Step 3: Analyze whether clarification is needed | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 103 | # Step 4: Route based on clarification analysis | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 149 | # Step 2: Generate structured research brief from user messages | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 156 | # Step 3: Initialize supervisor with research brief and instructions | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 212 | # Step 2: Generate supervisor response based on current context | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 216 | # Step 3: Update state and proceed to tool execution | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 413 | # Step 3: Generate researcher response with system context | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 417 | # Step 4: Update state and proceed to tool execution | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 525 | # Step 1: Configure the compression model | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 534 | # Step 2: Prepare messages for compression | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 540 | # Step 3: Attempt compression with retry logic for token limit issues | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 620 | # Step 1: Extract research findings and prepare state cleanup | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 625 | # Step 2: Configure the final report generation model | COMMENT |
| LOW⚡ | src/open_deep_research/deep_researcher.py | 634 | # Step 3: Attempt report generation with token limit retry logic | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 132 | # Step 1: Set up the research model for structured output | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 192 | # Step 1: Configure the supervisor model with available tools | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 240 | # Step 1: Extract current state and check exit conditions | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 264 | # Step 2: Process all tool calls together (both think_tool and ConductResearch) | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 344 | # Step 3: Return command with all tool results | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 379 | # Step 1: Load configuration and validate tool availability | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 391 | # Step 2: Configure the researcher model with tools | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 451 | # Step 1: Extract current state and check early exit conditions | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 466 | # Step 2: Handle other tool calls (search, MCP tools, etc.) | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 491 | # Step 3: Check late exit conditions (after processing tools) | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 576 | # Step 4: Return error result if all attempts failed | COMMENT |
| LOW | src/open_deep_research/deep_researcher.py | 692 | # Step 4: Return failure result if all retries exhausted | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | src/open_deep_research/utils.py | 502 | except Exception: | CODE |
| LOW | src/open_deep_research/utils.py | 210 | except Exception as e: | CODE |
| LOW | src/open_deep_research/utils.py | 288 | except Exception as e: | CODE |
| LOW | src/open_deep_research/deep_researcher.py | 332 | except Exception as e: | CODE |
| LOW | src/open_deep_research/deep_researcher.py | 431 | except Exception as e: | CODE |
| LOW | src/open_deep_research/deep_researcher.py | 565 | except Exception as e: | CODE |
| LOW | src/open_deep_research/deep_researcher.py | 661 | except Exception as e: | CODE |
| LOW | src/security/auth.py | 65 | except Exception as e: | CODE |
| MEDIUM | src/legacy/utils.py | 1098 | print(f"Error in Google search for '{query}': {str(e)}") | CODE |
| MEDIUM | src/legacy/utils.py | 1166 | print(f"Error in Google search for query '{query}': {str(e)}") | CODE |
| MEDIUM | src/legacy/utils.py | 607 | def process_single_query(query): | CODE |
| MEDIUM | src/legacy/utils.py | 765 | def process_single_query(query): | CODE |
| MEDIUM | src/legacy/utils.py | 1023 | def google_search(query, max_results): | CODE |
| MEDIUM | src/legacy/utils.py | 558 | print(f"Error processing query '{query}': {str(e)}") | STRING |
| MEDIUM | src/legacy/utils.py | 694 | print(f"Error processing arXiv query '{query}': {str(e)}") | STRING |
| MEDIUM | src/legacy/utils.py | 716 | print(f"Error processing arXiv query '{query}': {str(e)}") | STRING |
| LOW | src/legacy/utils.py | 1097 | except Exception as e: | CODE |
| LOW | src/legacy/utils.py | 1146 | except Exception as e: | CODE |
| LOW | src/legacy/utils.py | 1165 | except Exception as e: | CODE |
| LOW | src/legacy/utils.py | 1232 | except Exception as e: | CODE |
| LOW | src/legacy/utils.py | 1306 | except Exception as e: | CODE |
| LOW | src/legacy/utils.py | 556 | except Exception as e: | STRING |
| LOW | src/legacy/utils.py | 692 | except Exception as e: | STRING |
| LOW | src/legacy/utils.py | 714 | except Exception as e: | STRING |
| LOW | src/legacy/utils.py | 826 | except Exception as e: | STRING |
| LOW | src/legacy/utils.py | 862 | except Exception as e: | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/prompts.py | 1 | OVERALL_QUALITY_PROMPT = """You are an expert evaluator tasked with assessing the quality of research reports. Please ev | CODE |
| MEDIUM | .github/workflows/claude-code-review.yml | 62 | # - For React components: Performance, accessibility, and best practices | COMMENT |
| MEDIUM | .github/workflows/claude-code-review.yml | 69 | # 'Please provide a thorough code review focusing on our coding standards and best practices.' }} | COMMENT |
| MEDIUM | src/open_deep_research/prompts.py | 228 | final_report_generation_prompt = """Based on all the research conducted, create a comprehensive, well-structured answer | CODE |
| MEDIUM | src/open_deep_research/utils.py | 386 | """Wrap MCP tool with comprehensive authentication and error handling. | STRING |
| MEDIUM⚡ | src/open_deep_research/deep_researcher.py | 641 | # Create comprehensive prompt with all research context | COMMENT |
| MEDIUM | src/open_deep_research/deep_researcher.py | 608 | """Generate the final comprehensive research report with retry logic for token limits. | STRING |
| MEDIUM | src/legacy/prompts.py | 83 | query_writer_instructions="""You are an expert technical writer crafting targeted web search queries that will gather co | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/supervisor_parallel_evaluation.py | 10 | def right_parallelism_evaluator( | CODE |
| LOW⚡ | src/open_deep_research/utils.py | 703 | def _check_openai_token_limit(exception: Exception, error_str: str) -> bool: | CODE |
| LOW | src/open_deep_research/utils.py | 385 | def wrap_mcp_authenticate_tool(tool: StructuredTool) -> StructuredTool: | CODE |
| LOW | src/open_deep_research/utils.py | 399 | def _find_mcp_error_in_exception_chain(exc: BaseException) -> McpError | None: | CODE |
| LOW | src/open_deep_research/utils.py | 599 | def get_notes_from_tool_calls(messages: list[MessageLikeRepresentation]): | CODE |
| LOW | src/open_deep_research/utils.py | 607 | def anthropic_websearch_called(response): | CODE |
| LOW | src/open_deep_research/utils.py | 736 | def _check_anthropic_token_limit(exception: Exception, error_str: str) -> bool: | CODE |
| LOW | src/open_deep_research/utils.py | 759 | def _check_gemini_token_limit(exception: Exception, error_str: str) -> bool: | CODE |
| LOW | src/open_deep_research/utils.py | 848 | def remove_up_to_last_ai_message(messages: list[MessageLikeRepresentation]) -> list[MessageLikeRepresentation]: | CODE |
| LOW | src/legacy/multi_agent.py | 340 | async def supervisor_should_continue(state: ReportState) -> str: | CODE |
| LOW | src/legacy/multi_agent.py | 447 | async def research_agent_should_continue(state: SectionState) -> str: | CODE |
| LOW | src/legacy/graph.py | 396 | def gather_completed_sections(state: ReportState): | CODE |
| LOW | src/legacy/graph.py | 451 | def initiate_final_section_writing(state: ReportState): | CODE |
| LOW | src/legacy/utils.py | 89 | def deduplicate_and_format_sources( | CODE |
| LOW | src/legacy/utils.py | 1501 | async def select_and_execute_search(search_api: str, query_list: list[str], params_to_pass: dict) -> str: | CODE |
| LOW | src/legacy/utils.py | 1573 | def split_and_rerank_search_results(embeddings: Embeddings, query: str, search_results: list[dict], max_chunks: int = 5) | CODE |
| LOW | src/legacy/utils.py | 219 | async def azureaisearch_search_async(search_queries: list[str], max_results: int = 5, topic: str = "general", include_ra | STRING |
| LOW | src/legacy/tests/test_report_quality.py | 140 | def test_response_criteria_evaluation(research_agent, search_api, models, eval_model): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | src/open_deep_research/deep_researcher.py | 713 | # Define main workflow edges for sequential execution | COMMENT |
| MEDIUM | src/legacy/utils.py | 1207 | # Create an async HTTP client | COMMENT |
| MEDIUM⚡ | src/legacy/utils.py | 417 | # Define the function to process a single query | STRING |
| MEDIUM⚡ | src/legacy/utils.py | 422 | # Define the function for the executor with all parameters | STRING |
| MEDIUM | src/legacy/tests/test_report_quality.py | 147 | # Create a table for model configuration | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/open_deep_research/utils.py | 531 | CODE | |
| LOW | src/open_deep_research/utils.py | 665 | CODE | |
| LOW | src/open_deep_research/utils.py | 892 | CODE | |
| LOW | src/open_deep_research/deep_researcher.py | 607 | CODE | |
| LOW | src/legacy/multi_agent.py | 240 | CODE | |
| LOW | src/legacy/utils.py | 374 | CODE | |
| LOW | src/legacy/utils.py | 577 | CODE | |
| LOW | src/legacy/utils.py | 928 | CODE | |
| LOW | src/legacy/utils.py | 1188 | CODE | |
| LOW | src/legacy/utils.py | 1248 | CODE | |
| LOW | src/legacy/utils.py | 1501 | CODE | |
| LOW | src/legacy/utils.py | 418 | CODE | |
| LOW | src/legacy/utils.py | 607 | CODE | |
| LOW | src/legacy/utils.py | 967 | CODE | |
| LOW | src/legacy/utils.py | 1258 | CODE | |
| LOW | src/legacy/utils.py | 1262 | CODE | |
| LOW | src/legacy/utils.py | 1023 | CODE | |
| LOW | src/legacy/utils.py | 1118 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | src/legacy/CLAUDE.md | 61 | # Test only the multi-agent implementation | COMMENT |
| MEDIUM | src/legacy/tests/test_report_quality.py | 174 | # Checkpointer for the multi-agent approach | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | src/legacy/utils.py | 1502 | Select and execute the appropriate search API. Args: search_api: Name of the search API to use | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | src/open_deep_research/utils.py | 710 | # Check if this is an OpenAI exception | COMMENT |
| LOW | src/open_deep_research/utils.py | 743 | # Check if this is an Anthropic exception | COMMENT |
| LOW | src/open_deep_research/utils.py | 766 | # Check if this is a Google/Gemini exception | COMMENT |
| LOW⚡ | src/legacy/utils.py | 426 | # Set text to True if max_characters is None, otherwise use an object with max_characters | STRING |
| LOW | src/legacy/tests/run_test.py | 129 | # Display results with rich formatting | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | .github/workflows/claude-code-review.yml | 1 | name: Claude Code Review | COMMENT |
| LOW | .github/workflows/claude-code-review.yml | 61 | # - For API endpoints: Security, input validation, and error handling | COMMENT |
| LOW | .github/workflows/claude.yml | 41 | actions: read | COMMENT |
| LOW | .github/workflows/claude.yml | 61 | # Optional: Custom environment variables for Claude | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH⚡ | src/legacy/utils.py | 428 | "summary": True, # This is an amazing feature by EXA. It provides an AI generated summary of the conten | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/legacy/tests/conftest.py | 5 | CODE | |
| LOW | src/legacy/tests/test_report_quality.py | 13 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | src/security/auth.py | 51 | CODE |