From cf684ae221af0d1bf9a137f810bdd45ed0dc694c Mon Sep 17 00:00:00 2001 From: Sakshi3027 Date: Sun, 14 Jun 2026 01:05:33 -0400 Subject: [PATCH] feat: add structured output and tool calling examples for Crusoe Managed Inference --- structured-output-crusoe/.gitignore | 4 + structured-output-crusoe/README.md | 88 +++++++++++ structured-output-crusoe/requirements.txt | 5 + structured-output-crusoe/structured_output.py | 142 ++++++++++++++++++ 4 files changed, 239 insertions(+) create mode 100644 structured-output-crusoe/.gitignore create mode 100644 structured-output-crusoe/README.md create mode 100644 structured-output-crusoe/requirements.txt create mode 100644 structured-output-crusoe/structured_output.py diff --git a/structured-output-crusoe/.gitignore b/structured-output-crusoe/.gitignore new file mode 100644 index 0000000..0a16c91 --- /dev/null +++ b/structured-output-crusoe/.gitignore @@ -0,0 +1,4 @@ +.env +__pycache__/ +*.pyc +.DS_Store diff --git a/structured-output-crusoe/README.md b/structured-output-crusoe/README.md new file mode 100644 index 0000000..9cf43a9 --- /dev/null +++ b/structured-output-crusoe/README.md @@ -0,0 +1,88 @@ +# Structured Output & Tool Calling × Crusoe AI + +Pydantic-validated structured output and tool calling on [Crusoe Managed Inference](https://www.crusoe.ai/cloud/managed-inference) using `langchain-crusoe`. + +## What this demonstrates + +| Demo | What it shows | +|------|--------------| +| Structured summary | Extract typed fields from free-form LLM output | +| Code review | Validate verdict, score, and issue lists with Pydantic | +| Entity extraction | Pull companies, people, technologies, locations from text | +| Tool calling | Bind tools to the model and parse tool call arguments | + +## Prerequisites + +- Python 3.10+ +- A Crusoe Cloud account → [console.crusoecloud.com](https://console.crusoecloud.com) +- Inference API key (Intelligence API Keys section under Security) + +## Setup + +```bash +pip install -r requirements.txt +export CRUSOE_API_KEY="your-api-key" +``` + +## Run all demos + +```bash +python structured_output.py +``` + +## Local testing (no Crusoe account needed) + +```bash +pip install langchain-groq +export GROQ_API_KEY="your-groq-key" # free at console.groq.com +python structured_output.py +``` + +## How structured output works + +Define a Pydantic model and pass it to `with_structured_output`: + +```python +from pydantic import BaseModel, Field +from langchain_crusoe import ChatCrusoe + +class Summary(BaseModel): + title: str + key_points: list[str] + sentiment: str = Field(description="positive, neutral, or negative") + +llm = ChatCrusoe(model="meta-llama/Llama-3.3-70B-Instruct") +structured = llm.with_structured_output(Summary) +result = structured.invoke("Summarize the benefits of vector databases.") + +print(result.title) # typed str +print(result.key_points) # typed list[str] +print(result.sentiment) # typed str +``` + +## How tool calling works + +```python +from pydantic import BaseModel, Field +from langchain_crusoe import ChatCrusoe + +class SearchTool(BaseModel): + """Search for information on a topic.""" + query: str = Field(description="Search query") + max_results: int = Field(description="Number of results", default=5) + +llm = ChatCrusoe(model="meta-llama/Llama-3.3-70B-Instruct") +llm_with_tools = llm.bind_tools([SearchTool]) +response = llm_with_tools.invoke("Search for recent GPU benchmarks.") + +for call in response.tool_calls: + print(call["name"]) # SearchTool + print(call["args"]) # {"query": "recent GPU benchmarks", "max_results": 5} +``` + +## Related + +- [langchain-crusoe](../langchain-crusoe/) — LangChain integration for Crusoe Managed Inference +- [langgraph-crusoe](../langgraph-crusoe/) — Multi-node agentic pipelines on Crusoe +- [rag-crusoe](../rag-crusoe/) — RAG pipeline with Qdrant on Crusoe +- [Crusoe Managed Inference Docs](https://docs.crusoecloud.com/managed-inference/overview) diff --git a/structured-output-crusoe/requirements.txt b/structured-output-crusoe/requirements.txt new file mode 100644 index 0000000..eb0ac72 --- /dev/null +++ b/structured-output-crusoe/requirements.txt @@ -0,0 +1,5 @@ +langchain-crusoe>=0.1.0 +langchain-groq>=1.1.0 +langchain-core>=1.0.0 +pydantic>=2.0.0 +python-dotenv diff --git a/structured-output-crusoe/structured_output.py b/structured-output-crusoe/structured_output.py new file mode 100644 index 0000000..47321ba --- /dev/null +++ b/structured-output-crusoe/structured_output.py @@ -0,0 +1,142 @@ +""" +Structured output and tool calling on Crusoe Managed Inference. +Demonstrates Pydantic-validated responses and tool use with ChatCrusoe. +Tested locally with Groq as a drop-in replacement for Crusoe. +""" +import os +from dotenv import load_dotenv +from pydantic import BaseModel, Field +from langchain_core.messages import HumanMessage, SystemMessage + +load_dotenv() + + +def get_llm(): + if os.getenv("CRUSOE_API_KEY"): + from langchain_crusoe import ChatCrusoe + return ChatCrusoe( + model="meta-llama/Llama-3.3-70B-Instruct", + temperature=0, + max_tokens=1024, + ) + else: + from langchain_groq import ChatGroq + return ChatGroq( + model="llama-3.3-70b-versatile", + temperature=0, + max_tokens=1024, + ) + + +# --- Pydantic schemas --- + +class TechSummary(BaseModel): + """Structured summary of a technology or concept.""" + name: str = Field(description="Name of the technology") + category: str = Field(description="Category e.g. infrastructure, framework, database") + one_line: str = Field(description="One sentence explanation") + strengths: list[str] = Field(description="3 key strengths") + use_cases: list[str] = Field(description="3 common use cases") + maturity: str = Field(description="One of: experimental, growing, mature") + + +class CodeReview(BaseModel): + """Structured code review output.""" + verdict: str = Field(description="One of: approve, request_changes, comment") + issues: list[str] = Field(description="List of issues found, empty if none") + suggestions: list[str] = Field(description="List of improvement suggestions") + score: int = Field(description="Code quality score from 1 to 10", ge=1, le=10) + + +class EntityExtraction(BaseModel): + """Entities extracted from text.""" + companies: list[str] = Field(description="Company names mentioned") + technologies: list[str] = Field(description="Technologies or tools mentioned") + people: list[str] = Field(description="People mentioned") + locations: list[str] = Field(description="Locations mentioned") + + +# --- Tool definition --- + +class WeatherTool(BaseModel): + """Get current weather for a location.""" + location: str = Field(description="City and state, e.g. San Francisco, CA") + unit: str = Field(description="Temperature unit: celsius or fahrenheit", default="fahrenheit") + + +# --- Demo functions --- + +def demo_structured_summary(): + print("=" * 60) + print("DEMO 1: Structured output with Pydantic schema") + print("=" * 60) + llm = get_llm() + structured = llm.with_structured_output(TechSummary) + result = structured.invoke("Summarize Qdrant as a technology.") + print(f"Name: {result.name}") + print(f"Category: {result.category}") + print(f"Summary: {result.one_line}") + print(f"Maturity: {result.maturity}") + print(f"Strengths: {result.strengths}") + print(f"Use cases: {result.use_cases}") + + +def demo_code_review(): + print("\n" + "=" * 60) + print("DEMO 2: Structured code review") + print("=" * 60) + code = """ +def get_user(id): + query = "SELECT * FROM users WHERE id = " + id + return db.execute(query) +""" + llm = get_llm() + reviewer = llm.with_structured_output(CodeReview) + result = reviewer.invoke(f"Review this Python code:\n{code}") + print(f"Verdict: {result.verdict}") + print(f"Score: {result.score}/10") + print(f"Issues: {result.issues}") + print(f"Suggestions: {result.suggestions}") + + +def demo_entity_extraction(): + print("\n" + "=" * 60) + print("DEMO 3: Entity extraction") + print("=" * 60) + text = ( + "Crusoe Cloud, founded in San Francisco, uses NVIDIA GPUs and Kubernetes " + "to power AI workloads. CEO Chase Lochmiller recently announced a partnership " + "with Meta to run Llama models on their Wyoming data center." + ) + llm = get_llm() + extractor = llm.with_structured_output(EntityExtraction) + result = extractor.invoke(f"Extract all entities from this text:\n{text}") + print(f"Companies: {result.companies}") + print(f"Technologies: {result.technologies}") + print(f"People: {result.people}") + print(f"Locations: {result.locations}") + + +def demo_tool_calling(): + print("\n" + "=" * 60) + print("DEMO 4: Tool calling") + print("=" * 60) + llm = get_llm() + llm_with_tools = llm.bind_tools([WeatherTool]) + response = llm_with_tools.invoke([ + SystemMessage(content="You are a helpful assistant with access to weather tools."), + HumanMessage(content="What's the weather like in Austin, TX?"), + ]) + if response.tool_calls: + for call in response.tool_calls: + print(f"Tool called: {call['name']}") + print(f"Arguments: {call['args']}") + else: + print(response.content) + + +if __name__ == "__main__": + demo_structured_summary() + demo_code_review() + demo_entity_extraction() + demo_tool_calling()