Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/codex-pr-review.yml.disabled
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Codex PR Review
on:
pull_request:
types: [opened]

jobs:
codex:
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
final_message: ${{ steps.run_codex.outputs.final-message }}
steps:
- uses: actions/checkout@v5
with:
# Explicitly check out the PR's merge commit.
ref: refs/pull/${{ github.event.pull_request.number }}/merge

- name: Pre-fetch base and head refs for the PR
run: |
git fetch --no-tags origin \
${{ github.event.pull_request.base.ref }} \
+refs/pull/${{ github.event.pull_request.number }}/head

- name: Run Codex
id: run_codex
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt: |
This is PR #${{ github.event.pull_request.number }} for ${{ github.repository }}.

Review ONLY the changes introduced by the PR, so consider:
git log --oneline ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}

Suggest any improvements, potential bugs, or issues.
Be concise and specific in your feedback.

Pull request title and body:
----
${{ github.event.pull_request.title }}
${{ github.event.pull_request.body }}

post_feedback:
runs-on: ubuntu-latest
needs: codex
if: needs.codex.outputs.final_message != ''
permissions:
issues: write
pull-requests: write
steps:
- name: Report Codex feedback
uses: actions/github-script@v7
env:
CODEX_FINAL_MESSAGE: ${{ needs.codex.outputs.final_message }}
with:
github-token: ${{ github.token }}
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: process.env.CODEX_FINAL_MESSAGE,
});
26 changes: 26 additions & 0 deletions .github/workflows/pr-tests-and-format.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: PR Tests and Formatting

on:
pull_request:

jobs:
lint-and-test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Set up uv
uses: astral-sh/setup-uv@v2
with:
python-version: "3.12"
cache: true

- name: Install dependencies
run: uv sync --frozen

- name: Check formatting with Black
run: uv run black . --check

- name: Run pytest
run: uv run pytest
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ dependencies = [
"trustcall",
"python-dotenv",
"pytest",
"PyYAML"
"PyYAML",
"black"
]

[tool.pytest.ini_options]
Expand Down
12 changes: 10 additions & 2 deletions src/game/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@

from . import nodes
from . import state
from . import llm_strategy
from .llm_strategy import llm_update_player_mindset, llm_generate_speech
from . import rules
from . import graph
from . import config

__all__ = ["nodes", "state", "llm_strategy", "rules", "graph", "config"]
__all__ = [
"nodes",
"state",
"llm_update_player_mindset",
"llm_generate_speech",
"rules",
"graph",
"config",
]
8 changes: 2 additions & 6 deletions src/game/llm_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,14 @@
from typing import List, Dict, Any, Sequence

from langchain_core.messages import HumanMessage, SystemMessage

from trustcall import create_extractor

from src.game.state import GameState, Speech, PlayerMindset, Suspicion, SelfBelief
from src.tools.llm import create_llm
from src.game.state import Speech, PlayerMindset, Suspicion, SelfBelief


# Game rules are now managed by the configuration system
# Use config.get_game_rules() to get game rules

# --- LLM Clients ---
llm_client = create_llm()


# --- Helper Functions ---

Expand Down
19 changes: 14 additions & 5 deletions src/game/nodes/player.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
from ..llm_strategy import (
llm_update_player_mindset,
llm_generate_speech,
llm_client, # Use the real LLM client
)
from ..metrics import metrics_collector
from src.tools.llm import create_llm
from ..state import (
GameState,
alive_players,
Expand All @@ -42,7 +42,14 @@
merge_probs,
)

_llm_client = llm_client

def _get_llm_client():
"""Create and return an LLM client instance.

This function provides lazy initialization of the LLM client,
creating it only when needed and allowing for runtime configuration.
"""
return create_llm()


def _get_player_context(state: GameState, player_id: str):
Expand Down Expand Up @@ -104,8 +111,9 @@ def player_speech(state: GameState, player_id: str) -> Dict[str, Any]:
private_state = cur_player_context["private"]
existing_player_mindset = private_state.playerMindset

llm_client = _get_llm_client()
updated_mindset = llm_update_player_mindset(
llm_client=_llm_client,
llm_client=llm_client,
my_word=my_word,
completed_speeches=state["completed_speeches"],
players=state["players"],
Expand All @@ -117,7 +125,7 @@ def player_speech(state: GameState, player_id: str) -> Dict[str, Any]:

# Generate speech using LLM
new_speech_text = llm_generate_speech(
llm_client=_llm_client,
llm_client=llm_client,
my_word=my_word,
self_belief=updated_mindset.self_belief,
suspicions=updated_mindset.suspicions,
Expand Down Expand Up @@ -236,8 +244,9 @@ def player_vote(state: GameState, player_id: str) -> Dict[str, Any]:
private_state = cur_player_context["private"]
existing_player_mindset = private_state.playerMindset

llm_client = _get_llm_client()
updated_mindset = llm_update_player_mindset(
llm_client=_llm_client,
llm_client=llm_client,
my_word=my_word,
completed_speeches=state["completed_speeches"],
players=state["players"],
Expand Down
14 changes: 12 additions & 2 deletions tests/test_player_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,17 @@ def base_player_state(player_id):
}


@patch("src.game.nodes.player._get_llm_client")
@patch("src.game.nodes.player.llm_generate_speech")
@patch("src.game.nodes.player.llm_update_player_mindset")
def test_player_speech(
mock_infer, mock_speech, player_id, base_player_state: GameState
mock_infer, mock_speech, mock_get_llm, player_id, base_player_state: GameState
):
"""Tests the player_speech node with mocked LLM calls."""
# Arrange: Configure mocks to return predictable values
mock_llm_client = MagicMock()
mock_get_llm.return_value = mock_llm_client

mock_infer.return_value = PlayerMindset(
self_belief=SelfBelief(role="civilian", confidence=0.9),
suspicions={"c": Suspicion(role="spy", confidence=0.7, reason="vague")},
Expand All @@ -98,14 +102,19 @@ def test_player_speech(
assert private_update.playerMindset.suspicions["c"].role == "spy"

# Verify mocks were called correctly
mock_get_llm.assert_called_once()
mock_infer.assert_called_once()
mock_speech.assert_called_once()


@patch("src.game.nodes.player._get_llm_client")
@patch("src.game.nodes.player.llm_update_player_mindset")
def test_player_vote(mock_infer, player_id, base_player_state: GameState):
def test_player_vote(mock_infer, mock_get_llm, player_id, base_player_state: GameState):
"""Tests the player_vote node with mocked LLM calls."""
# Arrange: Configure mocks
mock_llm_client = MagicMock()
mock_get_llm.return_value = mock_llm_client

mock_infer.return_value = PlayerMindset(
self_belief=SelfBelief(role="civilian", confidence=0.9),
suspicions={"c": Suspicion(role="spy", confidence=0.8, reason="very vague")},
Expand All @@ -129,6 +138,7 @@ def test_player_vote(mock_infer, player_id, base_player_state: GameState):
assert private_update.playerMindset.self_belief.role == "civilian"

# Verify mocks
mock_get_llm.assert_called_once()
mock_infer.assert_called_once()


Expand Down
54 changes: 54 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading