Quick Start =============================================================================== Installation ------------------------------------------------------------------------------- Install the base package plus the provider extras you need: .. code-block:: bash # Base (no provider) pip install core-genai # Individual providers pip install "core-genai[claude]" pip install "core-genai[chatgpt]" pip install "core-genai[gemini]" pip install "core-genai[grok]" # Multiple providers at once pip install "core-genai[gemini,claude,chatgpt,grok]" # Development (includes all test/lint tools) pip install -e ".[dev]" Usage ------------------------------------------------------------------------------- All agents share the same interface. Pick a provider, create an agent, and call ``analyze``. Async (recommended) ~~~~~~~~~~~~~~~~~~~ .. code-block:: python import asyncio from core_genai.interfaces import IAgent agent = IAgent.create_agent("GeminiAgent", api_key="YOUR_API_KEY") async def main(): output = await agent.analyze( model="gemini-2.0-flash", prompt="Explain how AI works in a few words.", ) print(agent.get_text(output)) asyncio.run(main()) Synchronous ~~~~~~~~~~~ Wrap any agent with ``SyncWrapper`` from ``core-mixins`` to use it synchronously without managing an event loop yourself: .. code-block:: python from core_mixins.decorators.async_ import SyncWrapper from core_genai.interfaces import IAgent agent = IAgent.create_agent("ClaudeAgent", api_key="YOUR_API_KEY") with SyncWrapper(agent) as sync_agent: output = sync_agent.analyze( model="claude-haiku-4-5", prompt="Explain how AI works in a few words.", ) print(agent.get_text(output)) Switching providers ~~~~~~~~~~~~~~~~~~~ Because every agent implements the same ``IAgent`` interface you can swap providers by changing a single line: .. code-block:: python # Gemini agent = IAgent.create_agent("GeminiAgent", api_key=GEMINI_KEY) output = await agent.analyze(model="gemini-2.0-flash", prompt=PROMPT) # Claude agent = IAgent.create_agent("ClaudeAgent", api_key=CLAUDE_KEY) output = await agent.analyze(model="claude-haiku-4-5", prompt=PROMPT) # ChatGPT agent = IAgent.create_agent("ChatGPTAgent", api_key=OPENAI_KEY) output = await agent.analyze(model="gpt-4o-mini", prompt=PROMPT) # Grok agent = IAgent.create_agent("GrokAgent", api_key=XAI_KEY) output = await agent.analyze(model="grok-3-mini", prompt=PROMPT) # The rest of the code is identical for all providers: print(agent.get_text(output)) print(agent.get_cost(output)) print(agent.get_metadata(output)) Usage metadata ~~~~~~~~~~~~~~~~~~~ ``get_metadata()`` returns a normalized :class:`~core_genai.base.UsageMetadata` dict regardless of provider: .. code-block:: python meta = agent.get_metadata(output) print(meta["input_tokens"]) # tokens sent print(meta["output_tokens"]) # tokens received print(meta["total_tokens"]) # combined print(meta["cost_usd"]) # estimated USD cost # Optional fields (present only when the provider reports them): meta.get("cached_tokens") # cache read/hit tokens (Gemini, Claude) meta.get("cache_creation_tokens") # cache write tokens (Claude only) meta.get("reasoning_tokens") # thinking tokens (Gemini only) Batch jobs ------------------------------------------------------------------------------- Gemini, Claude, ChatGPT, and Grok all implement the :class:`~core_genai.base.IScheduler` interface for large-scale asynchronous inference at reduced cost (~50% discount on most providers). Each agent accepts simple :class:`~core_genai.base.ScheduledJobMetadata` request objects; the provider-specific JSONL serialization is handled internally. .. code-block:: python import asyncio from core_genai.interfaces import IAgent, IScheduler from core_genai.chatgpt.agent import BatchRequest agent = IAgent.create_agent("ChatGPTAgent", api_key="YOUR_API_KEY") requests = [ BatchRequest(custom_id="req-0", prompt=[{"role": "user", "content": "What is ML?"}]), BatchRequest(custom_id="req-1", prompt=[{"role": "user", "content": "What is a neural net?"}]), ] async def main(): job = await agent.schedule_job(requests=requests, model="gpt-4o-mini") print(job["job_id"]) # Poll until done while True: state = await agent.check_job_status(job["job_id"]) if state in agent.TERMINAL_STATES: break await asyncio.sleep(30) result = await agent.extract_job_results(job["job_id"]) for item in result["results"] or []: print(item) asyncio.run(main()) The same pattern works for Gemini, Claude, and Grok by swapping the agent and request type. Results are returned via :class:`~core_genai.base.ScheduledJobResponse` with a normalized ``job_id``, ``results``, ``error``, and ``state`` field.