Skill: okaris/python-sdk (version 0.1.5)

--- name: python-sdk description: "Python SDK for inference.sh - run AI apps, build agents, and integrate with 150+ models. Package: inferencesh (pip install inferencesh). Supports sync/async, streaming, file uploads. Build agents with template or ad-hoc patterns, tool builder API, skills, and human approval. Use for: Python integration, AI apps, agent development, RAG pipelines, automation. Triggers: python sdk, inferencesh, pip install, python api, python client, async inference, python agent, tool builder python, programmatic ai, python integration, sdk python" allowed-tools: Bash(pip install inferencesh), Bash(python *) --- # Python SDK Build AI applications with the [inference.sh](https://inference.sh) Python SDK. ![Python SDK](https://cloud.inference.sh/app/files/u/4mg21r6ta37mpaz6ktzwtt8krr/01kgvftjwhby36trvaj66bwzcf.jpeg) ## Quick Start ```bash pip install inferencesh ``` ```python from inferencesh import inference client = inference([REDACTED_SECRET]") # Run an AI app result = client.run({ "app": "infsh/flux-schnell", "input": {"prompt": "A sunset over mountains"} }) print(result["output"]) ``` ## Installation ```bash # Standard installation pip install inferencesh # With async support pip install inferencesh[async] ``` **Requirements:** Python 3.8+ ## Authentication ```python import os from inferencesh import inference # Direct API key client = inference([REDACTED_SECRET]") # From environment variable (recommended) client = inference([REDACTED_SECRET]"INFERENCE_API_KEY"]) ``` Get your API key: Settings → API Keys → Create API Key ## Running Apps ### Basic Execution ```python result = client.run({ "app": "infsh/flux-schnell", "input": {"prompt": "A cat astronaut"} }) print(result["status"]) # "completed" print(result["output"]) # Output data ``` ### Fire and Forget ```python task = client.run({ "app": "google/veo-3-1-fast", "input": {"prompt": "Drone flying over mountains"} }, wait=False) print(f"Task ID: {task['id']}") # Check later with client.get_task(task['id']) ``` ### Streaming Progress ```python for update in client.run({ "app": "google/veo-3-1-fast", "input": {"prompt": "Ocean waves at sunset"} }, stream=True): print(f"Status: {update['status']}") if update.get("logs"): print(update["logs"][-1]) ``` ### Run Parameters | Parameter | Type | Description | |-----------|------|-------------| | `app` | string | App ID (namespace/name@version) | | `input` | dict | Input matching app schema | | `setup` | dict | Hidden setup configuration | | `infra` | string | 'cloud' or 'private' | | `session` | string | Session ID for stateful execution | | `session_timeout` | int | Idle timeout (1-3600 seconds) | ## File Handling ### Automatic Upload ```python result = client.run({ "app": "image-processor", "input": { "image": "/path/to/image.png" # Auto-uploaded } }) ``` ### Manual Upload ```python from inferencesh import UploadFileOptions # Basic upload file = client.upload_file("/path/to/image.png") # With options file = client.upload_file( "/path/to/image.png", UploadFileOptions( filename="custom_name.png", content_type="image/png", public=True ) ) result = client.run({ "app": "image-processor", "input": {"image": file["uri"]} }) ``` ## Sessions (Stateful Execution) Keep workers warm across multiple calls: ```python # Start new session result = client.run({ "app": "my-app", "input": {"action": "init"}, "session": "new", "session_timeout": 300 # 5 minutes }) session_id = result["session_id"] # Continue in same session result = client.run({ "app": "my-app", "input": {"action": "process"}, "session": session_id }) ``` ## Agent SDK ### Template Agents Use pre-built agents from your workspace: ```python agent = client.agent("my-team/support-agent@latest") # Send message response = agent.send_message("Hello!") print(response.text) # Multi-turn conversation response = agent.send_message("Tell me more") # Reset conversation agent.reset() # Get chat history chat = agent.get_chat() ``` ### Ad-hoc Agents Create custom agents programmatically: ```python from inferencesh import tool, string, number, app_tool # Define tools calculator = ( tool("calculate") .describe("Perform a calculation") .param("expression", string("Math expression")) .build() ) image_gen = ( app_tool("generate_image", "infsh/flux-schnell@latest") .describe("Generate an image") .param("prompt", string("Image description")) .build() ) # Create agent agent = client.agent({ "core_app": {"ref": "infsh/claude-sonnet-4@latest"}, "system_prompt": "You are a helpful assistant.", "tools": [calculator, image_gen], "temperature": 0.7, "max_tokens": 4096 }) response = agent.send_message("What is 25 * 4?") ``` ### Available Core Apps | Model | App Reference | |-------|---------------| | Claude Sonnet 4 | `infsh/claude-sonnet-4@latest` | | Claude 3.5 Haiku | `infsh/claude-haiku-35@latest` | | GPT-4o | `infsh/gpt-4o@latest` | | GPT-4o Mini | `infsh/gpt-4o-mini@latest` | ## Tool Builder API ### Parameter Types ```python from inferencesh import ( string, number, integer, boolean, enum_of, array, obj, optional ) name = string("User's name") age = integer("Age in years") score = number("Score 0-1") active = boolean("Is active") priority = enum_of(["low", "medium", "high"], "Priority") tags = array(string("Tag"), "List of tags") address = obj({ "street": string("Street"), "city": string("City"), "zip": optional(string("ZIP")) }, "Address") ``` ### Client Tools (Run in Your Code) ```python greet = ( tool("greet") .display("Greet User") .describe("Greets a user by name") .param("name", string("Name to greet")) .require_approval() .build() ) ``` ### App Tools (Call AI Apps) ```python generate = ( app_tool("generate_image", "infsh/flux-schnell@latest") .describe("Generate an image from text") .param("prompt", string("Image description")) .setup({"model": "schnell"}) .input({"steps": 20}) .require_approval() .build() ) ``` ### Agent Tools (Delegate to Sub-agents) ```python from inferencesh import agent_tool researcher = ( agent_tool("research", "my-org/researcher@v1") .describe("Research a topic") .param("topic", string("Topic to research")) .build() ) ``` ### Webhook Tools (Call External APIs) ```python from inferencesh import webhook_tool notify = ( webhook_tool("slack", "https://hooks.slack.com/...") .describe("Send Slack notification") .secret("SLACK_SECRET") .param("channel", string("Channel")) .param("message", string("Message")) .build() ) ``` ### Internal Tools (Built-in Capabilities) ```python from inferencesh import internal_tools config = ( internal_tools() .plan() .memory() .web_search(True) .code_execution(True) .image_generation({ "enabled": True, "app_ref": "infsh/flux@latest" }) .build() ) agent = client.agent({ "core_app": {"ref": "infsh/claude-sonnet-4@latest"}, "internal_tools": config }) ``` ## Streaming Agent Responses ```python def handle_message(msg): if msg.get("content"): print(msg["content"], end="", flush=True) def handle_tool(call): print(f"\n[Tool: {call.name}]") result = execute_tool(call.name, call.args) agent.submit_tool_result(call.id, result) response = agent.send_message( "Explain quantum computing", on_message=handle_message, on_tool_call=handle_tool ) ``` ## File Attachments ```python # From file path with open("image.png", "rb") as f: response = agent.send_message( "What's in this image?", files=[f.read()] ) # From base64 response = agent.send_message( "Analyze this", files=["data:image/png;base64,iVBORw0KGgo..."] ) ``` ## Skills (Reusable Context) ```python agent = client.agent({ "core_app": {"ref": "infsh/claude-sonnet-4@latest"}, "skills": [ { "name": "code-review", "description": "Code review guidelines", "content": "# Code Review\n\n1. Check security\n2. Check performance..." }, { "name": "api-docs", "description": "API documentation", "url": "https://example.com/skills/api-docs.md" } ] }) ``` ## Async Support ```python from inferencesh import async_inference import asyncio async def main(): client = async_inference([REDACTED_SECRET]") # Async app execution result = await client.run({ "app": "infsh/flux-schnell", "input": {"prompt": "A galaxy"} }) # Async agent agent = client.agent("my-org/assistant@latest") response = await agent.send_message("Hello!") # Async streaming async for msg in agent.stream_messages(): print(msg) asyncio.run(main()) ``` ## Error Handling ```python from inferencesh import RequirementsNotMetException try: result = client.run({"app": "my-app", "input": {...}}) except RequirementsNotMetException as e: print(f"Missing requirements:") for err in e.errors: print(f" - {err['type']}: {err['key']}") except RuntimeError as e: print(f"Error: {e}") ``` ## Human Approval Workflows ```python def handle_tool(call): if call.requires_approval: # Show to user, get confirmation approved = prompt_user(f"Allow {call.name}?") if approved: result = execute_tool(call.name, call.args) agent.submit_tool_result(call.id, result) else: agent.submit_tool_result(call.id, {"error": "Denied by user"}) response = agent.send_message( "Delete all temp files", on_tool_call=handle_tool ) ``` ## Reference Files - [Agent Patterns](references/agent-patterns.md) - Multi-agent, RAG, human-in-the-loop patterns - [Tool Builder](references/tool-builder.md) - Complete tool builder API reference - [Streaming](references/streaming.md) - Real-time progress updates and SSE handling - [File Handling](references/files.md) - Upload, download, and manage files - [Sessions](references/sessions.md) - Stateful execution with warm workers - [Async Patterns](references/async-patterns.md) - Parallel processing and async/await ## Related Skills ```bash # JavaScript SDK npx skills add inference-sh/skills@javascript-sdk # Full platform skill (all 150+ apps via CLI) npx skills add inference-sh/skills@inference-sh # LLM models npx skills add inference-sh/skills@llm-models # Image generation npx skills add inference-sh/skills@ai-image-generation ``` ## Documentation - [Python SDK Reference](https://inference.sh/docs/api/sdk-python) - Full API documentation - [Agent SDK Overview](https://inference.sh/docs/api/agent-sdk) - Building agents - [Tool Builder Reference](https://inference.sh/docs/api/agent-tools) - Creating tools - [Authentication](https://inference.sh/docs/api/authentication) - API key setup - [Streaming](https://inference.sh/docs/api/sdk/streaming) - Real-time updates - [File Uploads](https://inference.sh/docs/api/sdk/files) - File handling

path: references/agent-patterns.md
content: # Agent Patterns

Common patterns for building agents with the Python SDK.

## Multi-Agent Orchestration

Delegate tasks to specialized sub-agents:

```python
from inferencesh import inference, agent_tool, string

client = inference([REDACTED_SECRET]")

# Define sub-agents as tools
researcher = (
    agent_tool("research", "my-org/researcher@latest")
    .describe("Research a topic thoroughly")
    .param("topic", string("Topic to research"))
    .build()
)

writer = (
    agent_tool("write", "my-org/writer@latest")
    .describe("Write content based on research")
    .param("outline", string("Content outline"))
    .param("research", string("Research findings"))
    .build()
)

# Orchestrator agent
orchestrator = client.agent({
    "core_app": {"ref": "infsh/claude-sonnet-4@latest"},
    "system_prompt": """You are an orchestrator that:
1. Uses the research tool to gather information
2. Uses the write tool to create content
Coordinate between agents to produce high-quality output.""",
    "tools": [researcher, writer]
})

response = orchestrator.send_message("Create a blog post about AI agents")
```

## RAG Pattern (Retrieval-Augmented Generation)

Combine search with LLM responses:

```python
from inferencesh import inference, app_tool, string

client = inference([REDACTED_SECRET]")

# Search tool
search = (
    app_tool("search", "tavily/search-assistant@latest")
    .describe("Search the web for current information")
    .param("query", string("Search query"))
    .build()
)

# RAG agent
rag_agent = client.agent({
    "core_app": {"ref": "infsh/claude-sonnet-4@latest"},
    "system_prompt": """You help users with current information.
When asked about recent events or facts you're unsure about,
use the search tool to find accurate, up-to-date information.
Always cite your sources.""",
    "tools": [search]
})

response = rag_agent.send_message("What are the latest developments in quantum computing?")
```

## Code Execution Pattern

Agents that can write and run code:

```python
from inferencesh import inference, internal_tools

client = inference([REDACTED_SECRET]")

config = (
    internal_tools()
    .code_execution(True)
    .build()
)

coder = client.agent({
    "core_app": {"ref": "infsh/claude-sonnet-4@latest"},
    "system_prompt": """You are a Python coding assistant.
Write code to solve problems and execute it to verify it works.
Explain your approach and show the output.""",
    "internal_tools": config
})

response = coder.send_message("Calculate the first 20 Fibonacci numbers")
```

## Human-in-the-Loop Pattern

Require approval for sensitive operations:

```python
from inferencesh import inference, tool, string

client = inference([REDACTED_SECRET]")

# Tool requiring approval
delete_file = (
    tool("delete_file")
    .describe("Delete a file from the filesystem")
    .param("path", string("File path to delete"))
    .require_approval()
    .build()
)

def handle_tool(call):
    if call.requires_approval:
        print(f"\n⚠️  Agent wants to: {call.name}")
        print(f"   Arguments: {call.args}")
        confirm = input("Allow? (y/n): ")

        if confirm.lower() == 'y':
            result = execute_operation(call.name, call.args)
            agent.submit_tool_result(call.id, result)
        else:
            agent.submit_tool_result(call.id, {
                "error": "Operation denied by user"
            })
    else:
        result = execute_operation(call.name, call.args)
        agent.submit_tool_result(call.id, result)

agent = client.agent({
    "core_app": {"ref": "infsh/claude-sonnet-4@latest"},
    "tools": [delete_file]
})

response = agent.send_message(
    "Clean up temporary files in /tmp/myapp",
    on_tool_call=handle_tool
)
```

## Conversation Memory Pattern

Maintain context across sessions:

```python
import json
from inferencesh import inference

client = inference([REDACTED_SECRET]")

def save_chat(agent, filepath):
    chat = agent.get_chat()
    with open(filepath, 'w') as f:
        json.dump(chat, f)

def load_chat(agent, filepath):
    try:
        with open(filepath, 'r') as f:
            chat = json.load(f)
            # Restore conversation by replaying messages
            for msg in chat['messages']:
                if msg['role'] == 'user':
                    agent.send_message(msg['content'])
    except FileNotFoundError:
        pass

agent = client.agent("my-org/assistant@latest")

# Load previous conversation
load_chat(agent, "conversation.json")

# Continue conversation
response = agent.send_message("Continue where we left off")

# Save for next session
save_chat(agent, "conversation.json")
```

## Streaming with Progress UI

Real-time updates for better UX:

```python
from inferencesh import inference
import sys

client = inference([REDACTED_SECRET]")
agent = client.agent("my-org/assistant@latest")

def stream_handler(msg):
    if msg.get("content"):
        sys.stdout.write(msg["content"])
        sys.stdout.flush()

def tool_handler(call):
    print(f"\n🔧 Using tool: {call.name}")
    # Execute and return result
    result = execute_tool(call.name, call.args)
    agent.submit_tool_result(call.id, result)
    print("✅ Tool completed")

response = agent.send_message(
    "Generate a report on market trends",
    on_message=stream_handler,
    on_tool_call=tool_handler
)

print("\n\n📊 Report complete!")
```

## Error Recovery Pattern

Graceful handling of failures:

```python
from inferencesh import inference, RequirementsNotMetException
import time

client = inference([REDACTED_SECRET]")

def robust_run(config, max_retries=3):
    for attempt in range(max_retries):
        try:
            return client.run(config)
        except RequirementsNotMetException as e:
            print(f"Missing requirements: {e.errors}")
            raise
        except RuntimeError as e:
            if attempt < max_retries - 1:
                wait = 2 ** attempt
                print(f"Error: {e}. Retrying in {wait}s...")
                time.sleep(wait)
            else:
                raise

result = robust_run({
    "app": "infsh/flux-schnell",
    "input": {"prompt": "A serene landscape"}
})
```

## Batch Processing Pattern

Process multiple items efficiently:

```python
from inferencesh import async_inference
import asyncio

async def process_batch(items):
    client = async_inference([REDACTED_SECRET]")

    async def process_one(item):
        result = await client.run({
            "app": "infsh/flux-schnell",
            "input": {"prompt": item}
        })
        return result

    # Process in parallel with concurrency limit
    semaphore = asyncio.Semaphore(5)  # Max 5 concurrent

    async def bounded_process(item):
        async with semaphore:
            return await process_one(item)

    results = await asyncio.gather(*[
        bounded_process(item) for item in items
    ])
    return results

prompts = [
    "A mountain sunrise",
    "A city at night",
    "An ocean sunset",
    "A forest path"
]

results = asyncio.run(process_batch(prompts))
```


path: references/async-patterns.md
content: # Async Patterns Reference

Asynchronous programming with the Python SDK.

## Basic Async Client

```python
from inferencesh import async_inference
import asyncio

async def main():
    client = async_inference([REDACTED_SECRET]")

    result = await client.run({
        "app": "infsh/flux-schnell",
        "input": {"prompt": "A sunset"}
    })
    print(result["output"])

asyncio.run(main())
```

## Parallel Requests

Run multiple independent requests simultaneously:

```python
async def parallel_requests():
    client = async_inference([REDACTED_SECRET]")

    prompts = [
        "A mountain landscape",
        "An ocean sunset",
        "A forest path",
        "A city skyline"
    ]

    # Create all tasks
    tasks = [
        client.run({
            "app": "infsh/flux-schnell",
            "input": {"prompt": p}
        })
        for p in prompts
    ]

    # Run in parallel
    results = await asyncio.gather(*tasks)

    return results

results = asyncio.run(parallel_requests())
```

## Controlled Concurrency

Limit concurrent requests with semaphore:

```python
async def controlled_concurrency(items, max_concurrent=5):
    client = async_inference([REDACTED_SECRET]")
    semaphore = asyncio.Semaphore(max_concurrent)

    async def process_one(item):
        async with semaphore:
            return await client.run({
                "app": "processor",
                "input": {"data": item}
            })

    tasks = [process_one(item) for item in items]
    return await asyncio.gather(*tasks)

# Process 100 items, max 5 at a time
results = asyncio.run(controlled_concurrency(range(100), max_concurrent=5))
```

## Async Streaming

```python
async def stream_task():
    client = async_inference([REDACTED_SECRET]")

    async for update in client.run({
        "app": "google/veo-3-1-fast",
        "input": {"prompt": "Ocean waves"}
    }, stream=True):
        print(f"Status: {update['status']}")

        if update.get("status") == "completed":
            return update.get("output")

result = asyncio.run(stream_task())
```

## Async Agents

```python
async def agent_conversation():
    client = async_inference([REDACTED_SECRET]")
    agent = client.agent("my-org/assistant@latest")

    # Send message
    response = await agent.send_message("Hello!")
    print(response.text)

    # Streaming with callback
    async def on_message(msg):
        if msg.get("content"):
            print(msg["content"], end="", flush=True)

    response = await agent.send_message(
        "Tell me a story",
        on_message=on_message
    )

asyncio.run(agent_conversation())
```

## Producer-Consumer Pattern

```python
async def producer_consumer():
    client = async_inference([REDACTED_SECRET]")
    queue = asyncio.Queue()
    results = []

    async def producer(items):
        for item in items:
            await queue.put(item)
        # Signal end
        for _ in range(3):  # Number of consumers
            await queue.put(None)

    async def consumer(consumer_id):
        while True:
            item = await queue.get()
            if item is None:
                break

            result = await client.run({
                "app": "processor",
                "input": {"data": item}
            })
            results.append((item, result))
            print(f"Consumer {consumer_id} processed {item}")

    items = list(range(20))

    # Start producer and consumers
    await asyncio.gather(
        producer(items),
        consumer(1),
        consumer(2),
        consumer(3)
    )

    return results

results = asyncio.run(producer_consumer())
```

## Timeout Handling

```python
async def with_timeout():
    client = async_inference([REDACTED_SECRET]")

    try:
        result = await asyncio.wait_for(
            client.run({
                "app": "slow-app",
                "input": {"data": "..."}
            }),
            timeout=30.0  # 30 seconds
        )
        return result
    except asyncio.TimeoutError:
        print("Request timed out")
        return None

result = asyncio.run(with_timeout())
```

## Retry with Backoff

```python
async def retry_with_backoff(client, config, max_retries=3):
    for attempt in range(max_retries):
        try:
            return await client.run(config)
        except Exception as e:
            if attempt == max_retries - 1:
                raise

            wait = (2 ** attempt) + random.random()
            print(f"Attempt {attempt + 1} failed, retrying in {wait:.1f}s...")
            await asyncio.sleep(wait)

async def main():
    client = async_inference([REDACTED_SECRET]")

    result = await retry_with_backoff(client, {
        "app": "my-app",
        "input": {"data": "..."}
    })

asyncio.run(main())
```

## Batch Processing with Progress

```python
from tqdm.asyncio import tqdm

async def batch_with_progress(items):
    client = async_inference([REDACTED_SECRET]")
    semaphore = asyncio.Semaphore(10)

    async def process_one(item):
        async with semaphore:
            return await client.run({
                "app": "processor",
                "input": {"data": item}
            })

    tasks = [process_one(item) for item in items]

    results = []
    for coro in tqdm.as_completed(tasks, desc="Processing"):
        result = await coro
        results.append(result)

    return results

results = asyncio.run(batch_with_progress(range(100)))
```

## Context Manager Pattern

```python
class AsyncInferenceSession:
    def __init__(self, api_key):
        self.[REDACTED_SECRET]
        self.client = None

    async def __aenter__(self):
        self.client = async_inference([REDACTED_SECRET])
        return self.client

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        # Cleanup if needed
        pass

async def main():
    async with AsyncInferenceSession("inf_...") as client:
        result = await client.run({
            "app": "my-app",
            "input": {"data": "..."}
        })
        print(result)

asyncio.run(main())
```

## Error Aggregation

```python
async def process_with_errors(items):
    client = async_inference([REDACTED_SECRET]")

    async def safe_process(item):
        try:
            result = await client.run({
                "app": "processor",
                "input": {"data": item}
            })
            return {"success": True, "item": item, "result": result}
        except Exception as e:
            return {"success": False, "item": item, "error": str(e)}

    tasks = [safe_process(item) for item in items]
    results = await asyncio.gather(*tasks)

    successes = [r for r in results if r["success"]]
    failures = [r for r in results if not r["success"]]

    print(f"Succeeded: {len(successes)}, Failed: {len(failures)}")

    return successes, failures

asyncio.run(process_with_errors(range(50)))
```

## Async Generator Pattern

```python
async def stream_results(items):
    """Yield results as they complete."""
    client = async_inference([REDACTED_SECRET]")

    pending = set()

    for item in items:
        task = asyncio.create_task(client.run({
            "app": "processor",
            "input": {"data": item}
        }))
        task.item = item
        pending.add(task)

        # Limit pending tasks
        if len(pending) >= 10:
            done, pending = await asyncio.wait(
                pending,
                return_when=asyncio.FIRST_COMPLETED
            )
            for task in done:
                yield task.item, await task

    # Wait for remaining
    while pending:
        done, pending = await asyncio.wait(
            pending,
            return_when=asyncio.FIRST_COMPLETED
        )
        for task in done:
            yield task.item, await task

async def main():
    async for item, result in stream_results(range(100)):
        print(f"Completed: {item}")

asyncio.run(main())
```

## Integration with Web Frameworks

### FastAPI

```python
from fastapi import FastAPI
from inferencesh import async_inference

app = FastAPI()
client = async_inference([REDACTED_SECRET]")

@app.post("/generate")
async def generate(prompt: str):
    result = await client.run({
        "app": "infsh/flux-schnell",
        "input": {"prompt": prompt}
    })
    return {"image": result["output"]["url"]}
```

### aiohttp

```python
from aiohttp import web
from inferencesh import async_inference

client = async_inference([REDACTED_SECRET]")

async def handle_generate(request):
    data = await request.json()
    result = await client.run({
        "app": "infsh/flux-schnell",
        "input": {"prompt": data["prompt"]}
    })
    return web.json_response({"image": result["output"]["url"]})

app = web.Application()
app.router.add_post('/generate', handle_generate)
```


path: references/files.md
content: # File Handling Reference

Upload, download, and manage files with the Python SDK.

## Automatic File Upload

Local file paths in input are automatically uploaded:

```python
from inferencesh import inference

client = inference([REDACTED_SECRET]")

# File path is auto-uploaded
result = client.run({
    "app": "image-processor",
    "input": {
        "image": "/path/to/image.png"
    }
})
```

## Manual File Upload

### Basic Upload

```python
file = client.upload_file("/path/to/image.png")
print(file["uri"])  # inf://files/abc123

result = client.run({
    "app": "image-processor",
    "input": {"image": file["uri"]}
})
```

### Upload Options

```python
from inferencesh import UploadFileOptions

file = client.upload_file(
    "/path/to/document.pdf",
    UploadFileOptions(
        filename="custom_name.pdf",      # Custom filename
        content_type="application/pdf",   # MIME type
        path="/documents/reports",        # Storage path
        public=True                       # Publicly accessible
    )
)
```

## Supported Input Types

### File Path

```python
result = client.run({
    "app": "processor",
    "input": {"file": "/path/to/file.png"}
})
```

### Data URI (Base64)

```python
import base64

with open("image.png", "rb") as f:
    b64 = base64.b64encode(f.read()).decode()

result = client.run({
    "app": "processor",
    "input": {"image": f"data:image/png;base64,{b64}"}
})
```

### Bytes

```python
with open("image.png", "rb") as f:
    data = f.read()

file = client.upload_file(data, UploadFileOptions(
    filename="image.png",
    content_type="image/png"
))
```

### File Object

```python
with open("image.png", "rb") as f:
    file = client.upload_file(f, UploadFileOptions(
        filename="image.png",
        content_type="image/png"
    ))
```

## Working with URLs

Use remote URLs directly (no upload needed):

```python
result = client.run({
    "app": "image-processor",
    "input": {
        "image": "https://example.com/image.png"
    }
})
```

## Multiple Files

```python
# Upload multiple files
files = []
for path in ["/path/to/file1.png", "/path/to/file2.png"]:
    file = client.upload_file(path)
    files.append(file["uri"])

result = client.run({
    "app": "multi-file-processor",
    "input": {"images": files}
})
```

## File Info

```python
file = client.upload_file("/path/to/image.png")

print(f"URI: {file['uri']}")
print(f"URL: {file['url']}")  # Direct access URL
print(f"Size: {file['size']}")
print(f"Type: {file['content_type']}")
```

## Downloading Results

```python
import requests

result = client.run({
    "app": "infsh/flux-schnell",
    "input": {"prompt": "A sunset"}
})

# Result contains URL to generated file
image_url = result["output"]["image"]

# Download the file
response = requests.get(image_url)
with open("output.png", "wb") as f:
    f.write(response.content)
```

## Async File Operations

```python
from inferencesh import async_inference
import asyncio
import aiohttp

async def process_files():
    client = async_inference([REDACTED_SECRET]")

    # Upload
    file = await client.upload_file("/path/to/image.png")

    # Process
    result = await client.run({
        "app": "image-processor",
        "input": {"image": file["uri"]}
    })

    # Download result
    async with aiohttp.ClientSession() as session:
        async with session.get(result["output"]["url"]) as resp:
            data = await resp.read()
            with open("output.png", "wb") as f:
                f.write(data)

asyncio.run(process_files())
```

## Agent File Attachments

```python
agent = client.agent("my-org/assistant@latest")

# From bytes
with open("image.png", "rb") as f:
    response = agent.send_message(
        "What's in this image?",
        files=[f.read()]
    )

# From base64
response = agent.send_message(
    "Analyze this document",
    files=["data:application/pdf;base64,JVBERi0xLj..."]
)

# Multiple files
with open("img1.png", "rb") as f1, open("img2.png", "rb") as f2:
    response = agent.send_message(
        "Compare these images",
        files=[f1.read(), f2.read()]
    )
```

## Large File Handling

For large files, use chunked upload:

```python
def upload_large_file(client, filepath, chunk_size=5*1024*1024):
    """Upload large file in chunks (5MB default)."""
    import os

    file_size = os.path.getsize(filepath)
    filename = os.path.basename(filepath)

    with open(filepath, 'rb') as f:
        # Initialize multipart upload
        upload = client.create_multipart_upload(
            filename=filename,
            content_type="application/octet-stream",
            size=file_size
        )

        parts = []
        part_number = 1

        while True:
            chunk = f.read(chunk_size)
            if not chunk:
                break

            part = client.upload_part(
                upload_id=upload["id"],
                part_number=part_number,
                data=chunk
            )
            parts.append(part)
            part_number += 1

        # Complete upload
        file = client.complete_multipart_upload(
            upload_id=upload["id"],
            parts=parts
        )

        return file
```

## Content Type Detection

```python
import mimetypes

def upload_with_auto_type(client, filepath):
    content_type, _ = mimetypes.guess_type(filepath)

    return client.upload_file(
        filepath,
        UploadFileOptions(
            content_type=content_type or "application/octet-stream"
        )
    )
```

## Temporary Files

```python
import tempfile
import os

# Create temp file for processing
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
    tmp.write(image_data)
    tmp_path = tmp.name

try:
    result = client.run({
        "app": "image-processor",
        "input": {"image": tmp_path}
    })
finally:
    os.unlink(tmp_path)  # Clean up
```

## Error Handling

```python
from inferencesh import FileUploadError

try:
    file = client.upload_file("/path/to/large_file.bin")
except FileUploadError as e:
    if "too large" in str(e):
        print("File exceeds size limit")
    elif "unsupported" in str(e):
        print("File type not supported")
    else:
        print(f"Upload failed: {e}")
```


path: references/sessions.md
content: # Sessions Reference

Stateful execution with warm workers.

## What Are Sessions?

Sessions keep workers warm between requests, enabling:

- **Faster execution** - No cold start on subsequent calls
- **Shared state** - Maintain context, loaded models, cached data
- **Cost efficiency** - Reuse initialized resources

## Creating a Session

```python
from inferencesh import inference

client = inference([REDACTED_SECRET]")

# Start new session
result = client.run({
    "app": "my-app",
    "input": {"action": "initialize"},
    "session": "new"
})

session_id = result["session_id"]
print(f"Session: {session_id}")
```

## Using an Existing Session

```python
# Continue in same session
result = client.run({
    "app": "my-app",
    "input": {"action": "process", "data": "..."},
    "session": session_id
})
```

## Session Timeout

Set how long idle sessions stay alive (1-3600 seconds):

```python
# 5-minute timeout
result = client.run({
    "app": "my-app",
    "input": {"action": "init"},
    "session": "new",
    "session_timeout": 300
})
```

## Session Lifecycle

```
1. Create session (session: "new")
   ↓
2. Worker starts, initializes app
   ↓
3. Subsequent calls reuse worker (session: session_id)
   ↓
4. Idle timeout reached or explicit close
   ↓
5. Worker terminates
```

## Use Cases

### Model Loading

Load a model once, use it multiple times:

```python
# Initial load (slow)
result = client.run({
    "app": "ml-inference",
    "input": {"action": "load_model", "model": "large-model-v2"},
    "session": "new",
    "session_timeout": 600
})
session_id = result["session_id"]

# Fast inference calls
for item in data_batch:
    result = client.run({
        "app": "ml-inference",
        "input": {"action": "predict", "data": item},
        "session": session_id
    })
    print(result["output"])
```

### Browser Automation

Keep browser open across multiple actions:

```python
# Start browser session
result = client.run({
    "app": "browser-automation",
    "input": {"action": "start", "url": "https://example.com"},
    "session": "new",
    "session_timeout": 300
})
session_id = result["session_id"]

# Navigate
client.run({
    "app": "browser-automation",
    "input": {"action": "click", "selector": "#login-btn"},
    "session": session_id
})

# Fill form
client.run({
    "app": "browser-automation",
    "input": {"action": "type", "selector": "#username", "text": "[REDACTED_SECRET]"},
    "session": session_id
})

# Take screenshot
result = client.run({
    "app": "browser-automation",
    "input": {"action": "screenshot"},
    "session": session_id
})
```

### Stateful Conversations

```python
# Initialize chat context
result = client.run({
    "app": "chat-with-memory",
    "input": {"action": "init", "system": "You are a helpful assistant."},
    "session": "new",
    "session_timeout": 1800  # 30 minutes
})
session_id = result["session_id"]

# Multi-turn conversation
messages = [
    "What is quantum computing?",
    "Can you give me a simple example?",
    "How is it different from classical computing?"
]

for msg in messages:
    result = client.run({
        "app": "chat-with-memory",
        "input": {"message": msg},
        "session": session_id
    })
    print(f"Assistant: {result['output']['response']}")
```

### Data Processing Pipeline

```python
# Load data once
result = client.run({
    "app": "data-processor",
    "input": {"action": "load", "dataset": "large_dataset.parquet"},
    "session": "new",
    "session_timeout": 900
})
session_id = result["session_id"]

# Run multiple analyses
analyses = ["summary", "correlations", "outliers", "trends"]

for analysis in analyses:
    result = client.run({
        "app": "data-processor",
        "input": {"action": "analyze", "type": analysis},
        "session": session_id
    })
    print(f"{analysis}: {result['output']}")
```

## Session Management

### Check Session Status

```python
# Sessions are implicitly active when used
# If session expired, you'll get an error
try:
    result = client.run({
        "app": "my-app",
        "input": {"action": "check"},
        "session": session_id
    })
except Exception as e:
    if "session not found" in str(e).lower():
        print("Session expired, creating new one")
        # Create new session
```

### Explicit Session Close

```python
# Close session to free resources
client.run({
    "app": "my-app",
    "input": {"action": "cleanup"},
    "session": session_id
})
# Session will terminate after this call
```

### Session Recovery Pattern

```python
class SessionManager:
    def __init__(self, client, app, timeout=300):
        self.client = client
        self.app = app
        self.timeout = timeout
        self.session_id = None

    def ensure_session(self):
        if self.session_id is None:
            result = self.client.run({
                "app": self.app,
                "input": {"action": "init"},
                "session": "new",
                "session_timeout": self.timeout
            })
            self.session_id = result["session_id"]
        return self.session_id

    def run(self, input_data):
        try:
            return self.client.run({
                "app": self.app,
                "input": input_data,
                "session": self.ensure_session()
            })
        except Exception as e:
            if "session" in str(e).lower():
                # Session expired, create new one
                self.session_id = None
                return self.client.run({
                    "app": self.app,
                    "input": input_data,
                    "session": self.ensure_session()
                })
            raise

# Usage
manager = SessionManager(client, "my-app", timeout=600)
result = manager.run({"action": "process", "data": "..."})
```

## Async Sessions

```python
from inferencesh import async_inference
import asyncio

async def session_workflow():
    client = async_inference([REDACTED_SECRET]")

    # Create session
    result = await client.run({
        "app": "my-app",
        "input": {"action": "init"},
        "session": "new",
        "session_timeout": 300
    })
    session_id = result["session_id"]

    # Run operations
    tasks = [
        client.run({
            "app": "my-app",
            "input": {"action": "process", "id": i},
            "session": session_id
        })
        for i in range(10)
    ]

    # Note: These run sequentially on the same worker
    results = []
    for task in tasks:
        results.append(await task)

    return results

asyncio.run(session_workflow())
```

## Best Practices

1. **Set appropriate timeouts** - Balance between keeping workers warm and resource usage
2. **Handle session expiry** - Always catch and handle session not found errors
3. **Clean up when done** - Close sessions explicitly if you know you're finished
4. **Don't over-parallelize** - Session requests go to the same worker sequentially
5. **Monitor costs** - Long-running sessions incur ongoing charges


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (CRITICAL)