import os import asyncio from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, SystemMessage from langgraph.graph import StateGraph, START, END from typing import TypedDict, Annotated from langgraph.graph.message import add_messages from pydantic import BaseModel, Field from langchain_core.output_parsers import PydanticOutputParser # LLM setup llm = ChatOpenAI( model="gpt-4o-mini", base_url="https://openrouter.ai/api/v1", api_key=os.getenv("OPENAI_API_KEY"), temperature=0.0, ) # State definition class CodeReviewState(TypedDict): code: str draft_review: str criteria_scores: dict[str, int] weakest_criterion: str verdict: str round: int max_rounds: int # Node: draft_review async def draft_review(state: CodeReviewState): prompt = f"Write a concise code review (3-6 points) for the following Python function:\n\n{state['code']}" msg = await llm.ainvoke([HumanMessage(content=prompt)]) state['draft_review'] = msg.content return state # Node: reflect class ReflectOutput(BaseModel): scores: dict[str, int] weakest: str verdict: str parser = PydanticOutputParser(pydantic_object=ReflectOutput) async def reflect(state: CodeReviewState): prompt = f"Evaluate the draft review and assign scores 0-10 for PEP8, type_hints, edge_cases, naming. Return JSON with keys scores, weakest, verdict (ok or needs_revision).\n\nDraft review:\n{state['draft_review']}" msg = await llm.ainvoke([HumanMessage(content=prompt)]) out = parser.parse(msg.content) state['criteria_scores'] = out.scores state['weakest_criterion'] = out.weakest state['verdict'] = out.verdict return state # Node: rewrite async def rewrite(state: CodeReviewState): prompt = f"Rewrite the part of the draft review that addresses the weakest criterion '{state['weakest_criterion']}'. Keep other points unchanged.\n\nOriginal draft:\n{state['draft_review']}" msg = await llm.ainvoke([HumanMessage(content=prompt)]) state['draft_review'] = msg.content state['round'] += 1 return state # Graph graph = StateGraph(CodeReviewState) graph.add_node("draft_review", draft_review) graph.add_node("reflect", reflect) graph.add_node("rewrite", rewrite) graph.set_entry_point("draft_review") graph.add_edge("draft_review", "reflect") graph.add_conditional_edges( "reflect", lambda s: "rewrite" if s['verdict']=='needs_revision' and s['round']