update readme

This commit is contained in:
2025-11-10 09:40:52 -05:00
parent 631b2c44d8
commit f07973f86c
12 changed files with 49 additions and 26 deletions

View File

@@ -35,7 +35,7 @@ CODEX_API_KEY="<YOUR_CODEX_API_KEY>"
### 2. Install dependencies ### 2. Install dependencies
```bash ```bash
pip install modaic dspy python-dotenv uv add modaic dspy
``` ```
### 3. Use the agent ### 3. Use the agent
@@ -44,9 +44,6 @@ pip install modaic dspy python-dotenv
from modaic import AutoAgent from modaic import AutoAgent
import dspy import dspy
from pydantic import BaseModel from pydantic import BaseModel
from dotenv import load_dotenv
load_dotenv()
class Dependencies(BaseModel): class Dependencies(BaseModel):
dependencies: list[str] dependencies: list[str]

20
main.py
View File

@@ -1,17 +1,19 @@
from dotenv import load_dotenv
from src.index import CodexAgent, CodexAgentConfig from src.index import CodexAgent, CodexAgentConfig
load_dotenv()
codex_agent = CodexAgent(CodexAgentConfig()) codex_agent = CodexAgent(CodexAgentConfig())
def main(): def main():
codex_agent.push_to_hub("darinkishore/codex-agent", with_code=True, commit_message="add thread id property") codex_agent.push_to_hub(
#result = codex_agent(message="What files are in this directory?") "darinkishore/codex-agent",
#print(result.answer) # String response with_code=True,
#print(result.trace) # Execution items (commands, files, etc.) commit_message="update readme",
#print(result.usage) # Token counts )
# result = codex_agent(message="What files are in this directory?")
# print(result.answer) # String response
# print(result.trace) # Execution items (commands, files, etc.)
# print(result.usage) # Token counts
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -17,6 +17,8 @@ class Codex:
thread_options = options or ThreadOptions() thread_options = options or ThreadOptions()
return Thread(self._exec, self._options, thread_options) return Thread(self._exec, self._options, thread_options)
def resume_thread(self, thread_id: str, options: Optional[ThreadOptions] = None) -> Thread: def resume_thread(
self, thread_id: str, options: Optional[ThreadOptions] = None
) -> Thread:
thread_options = options or ThreadOptions() thread_options = options or ThreadOptions()
return Thread(self._exec, self._options, thread_options, thread_id) return Thread(self._exec, self._options, thread_options, thread_id)

View File

@@ -6,6 +6,7 @@ from typing import Mapping, Optional, TYPE_CHECKING
if TYPE_CHECKING: # pragma: no cover - typing only if TYPE_CHECKING: # pragma: no cover - typing only
from pydantic import BaseModel as PydanticBaseModel from pydantic import BaseModel as PydanticBaseModel
SchemaInput = Mapping[str, object] | type[PydanticBaseModel] | PydanticBaseModel SchemaInput = Mapping[str, object] | type[PydanticBaseModel] | PydanticBaseModel
else: else:
SchemaInput = Mapping[str, object] SchemaInput = Mapping[str, object]

View File

@@ -100,7 +100,9 @@ def _parse_usage(payload: object) -> Usage:
data = _ensure_dict(payload) data = _ensure_dict(payload)
return Usage( return Usage(
input_tokens=_ensure_int(data.get("input_tokens"), "input_tokens"), input_tokens=_ensure_int(data.get("input_tokens"), "input_tokens"),
cached_input_tokens=_ensure_int(data.get("cached_input_tokens"), "cached_input_tokens"), cached_input_tokens=_ensure_int(
data.get("cached_input_tokens"), "cached_input_tokens"
),
output_tokens=_ensure_int(data.get("output_tokens"), "output_tokens"), output_tokens=_ensure_int(data.get("output_tokens"), "output_tokens"),
) )

View File

@@ -26,7 +26,9 @@ class SpawnError(CodexError):
def __init__(self, command: Sequence[str] | None, error: OSError) -> None: def __init__(self, command: Sequence[str] | None, error: OSError) -> None:
self.command = list(command) if command else None self.command = list(command) if command else None
self.original_error = error self.original_error = error
super().__init__(f"Failed to spawn codex exec: {_format_command(self.command)}: {error}") super().__init__(
f"Failed to spawn codex exec: {_format_command(self.command)}: {error}"
)
@dataclass(slots=True) @dataclass(slots=True)

View File

@@ -82,6 +82,7 @@ class CodexExec:
stderr_thread: Thread | None = None stderr_thread: Thread | None = None
if process.stderr: if process.stderr:
def _drain_stderr(pipe: io.TextIOBase, buffer: list[str]) -> None: def _drain_stderr(pipe: io.TextIOBase, buffer: list[str]) -> None:
while True: while True:
try: try:

View File

@@ -171,12 +171,16 @@ def parse_thread_item(payload: object) -> ThreadItem:
if type_name == "command_execution": if type_name == "command_execution":
command = _ensure_str(payload.get("command"), "command") command = _ensure_str(payload.get("command"), "command")
aggregated_output = _ensure_str(payload.get("aggregated_output"), "aggregated_output") aggregated_output = _ensure_str(
payload.get("aggregated_output"), "aggregated_output"
)
status_str = _ensure_str(payload.get("status"), "status") status_str = _ensure_str(payload.get("status"), "status")
try: try:
status = CommandExecutionStatus(status_str) status = CommandExecutionStatus(status_str)
except ValueError as exc: except ValueError as exc:
raise CodexError(f"Unsupported command execution status: {status_str}") from exc raise CodexError(
f"Unsupported command execution status: {status_str}"
) from exc
exit_code = payload.get("exit_code") exit_code = payload.get("exit_code")
exit_value = int(exit_code) if isinstance(exit_code, int) else None exit_value = int(exit_code) if isinstance(exit_code, int) else None
return CommandExecutionItem( return CommandExecutionItem(

View File

@@ -23,7 +23,11 @@ def _get_pydantic_base_model() -> Type[Any] | None: # pragma: no cover - import
def _is_pydantic_model(value: object) -> bool: def _is_pydantic_model(value: object) -> bool:
base_model = _get_pydantic_base_model() base_model = _get_pydantic_base_model()
return isinstance(value, type) and base_model is not None and issubclass(value, base_model) return (
isinstance(value, type)
and base_model is not None
and issubclass(value, base_model)
)
def _is_pydantic_instance(value: object) -> bool: def _is_pydantic_instance(value: object) -> bool:

View File

@@ -53,11 +53,15 @@ class Thread:
def id(self) -> Optional[str]: def id(self) -> Optional[str]:
return self._id return self._id
def run_streamed(self, prompt: str, turn_options: Optional[TurnOptions] = None) -> ThreadStream: def run_streamed(
self, prompt: str, turn_options: Optional[TurnOptions] = None
) -> ThreadStream:
events = self._stream_events(prompt, turn_options) events = self._stream_events(prompt, turn_options)
return ThreadStream(events=events) return ThreadStream(events=events)
def run(self, prompt: str, turn_options: Optional[TurnOptions] = None) -> ThreadRunResult: def run(
self, prompt: str, turn_options: Optional[TurnOptions] = None
) -> ThreadRunResult:
final_response = "" final_response = ""
items: list[ThreadItem] = [] items: list[ThreadItem] = []
usage: Optional[Usage] = None usage: Optional[Usage] = None

View File

@@ -170,7 +170,9 @@ class CodexModule(dspy.Module):
if not _is_str_type(self.output_type): if not _is_str_type(self.output_type):
# Parse as Pydantic model # Parse as Pydantic model
try: try:
parsed_output = self.output_type.model_validate_json(result.final_response) parsed_output = self.output_type.model_validate_json(
result.final_response
)
except Exception as e: except Exception as e:
# Provide helpful error with response preview # Provide helpful error with response preview
response_preview = result.final_response[:500] response_preview = result.final_response[:500]

View File

@@ -6,6 +6,7 @@ from dspy.signatures.signature import Signature
from dspy.primitives.prediction import Prediction from dspy.primitives.prediction import Prediction
from typing import Optional from typing import Optional
class CodexAgentConfig(PrecompiledConfig): class CodexAgentConfig(PrecompiledConfig):
signature: str | type[Signature] = "message:str -> answer:str" signature: str | type[Signature] = "message:str -> answer:str"
working_directory: str = "." working_directory: str = "."
@@ -16,8 +17,9 @@ class CodexAgentConfig(PrecompiledConfig):
base_url: Optional[str] = None base_url: Optional[str] = None
codex_path_override: Optional[str] = "/opt/homebrew/bin/codex" codex_path_override: Optional[str] = "/opt/homebrew/bin/codex"
class CodexAgent(PrecompiledAgent): class CodexAgent(PrecompiledAgent):
config : CodexAgentConfig config: CodexAgentConfig
def __init__(self, config: CodexAgentConfig, **kwargs): def __init__(self, config: CodexAgentConfig, **kwargs):
super().__init__(config, **kwargs) super().__init__(config, **kwargs)
@@ -30,9 +32,9 @@ class CodexAgent(PrecompiledAgent):
skip_git_repo_check=config.skip_git_repo_check, skip_git_repo_check=config.skip_git_repo_check,
api_key=config.api_key, api_key=config.api_key,
base_url=config.base_url, base_url=config.base_url,
codex_path_override=config.codex_path_override codex_path_override=config.codex_path_override,
) )
def forward(self, **kwargs) -> Prediction: def forward(self, **kwargs) -> Prediction:
return self.codex_module(**kwargs) return self.codex_module(**kwargs)
@@ -46,4 +48,4 @@ class CodexAgent(PrecompiledAgent):
Returns: Returns:
Thread ID string, or None if no forward() calls have been made yet Thread ID string, or None if no forward() calls have been made yet
""" """
return self.codex_module.thread.id return self.codex_module.thread.id