### Quick Start: Basic Model Interaction Source: https://github.com/blacktop/fm-rs/blob/main/bindings/python/README.md Initialize the default system language model, check its availability, create a session, and send a prompt to get a response. ```python import fm # Create the default system language model model = fm.SystemLanguageModel() # Check availability if not model.is_available: print("Apple Intelligence is not available") exit(1) # Create a session session = fm.Session(model, instructions="You are a helpful assistant.") # Send a prompt response = session.respond("What is the capital of France?") print(response.content) ``` -------------------------------- ### Run fm-rs Examples Source: https://github.com/blacktop/fm-rs/blob/main/README.md Execute provided examples using Cargo. These examples demonstrate various features like basic usage, streaming responses, tool calling, structured output, and context compaction. ```bash cargo run --example basic ``` ```bash cargo run --example streaming ``` ```bash cargo run --example tools ``` ```bash cargo run --example structured ``` ```bash cargo run --example context_compaction ``` -------------------------------- ### Install fm-rs from Source Source: https://github.com/blacktop/fm-rs/blob/main/bindings/python/README.md Install fm-rs from source using maturin. This requires the Rust toolchain to be installed. ```bash # Requires Rust toolchain cd bindings/python uv sync uv run maturin develop ``` -------------------------------- ### Quick Start: Interact with Language Model Source: https://github.com/blacktop/fm-rs/blob/main/README.md Initialize a language model session, ensure it's available, and send a prompt to get a response. Requires macOS 26+, Apple Intelligence, Apple Silicon, and Rust 1.85+. ```rust use fm_rs::{GenerationOptions, Session, SystemLanguageModel}; fn main() -> Result<(), fm_rs::Error> { let model = SystemLanguageModel::new()?; model.ensure_available()?; let session = Session::with_instructions(&model, "You are a helpful assistant.")?; let options = GenerationOptions::builder() .temperature(0.7) .max_response_tokens(500) .build(); let response = session.respond("What is the capital of France?", &options)?; println!("{}", response.content()); Ok(()) } ``` -------------------------------- ### Python Bindings: Core Functionality Source: https://context7.com/blacktop/fm-rs/llms.txt Overview of the Python `fm` package, demonstrating basic session setup, response generation, streaming, and context window usage. ```APIDOC ## Python Bindings (`import fm`) The `fm` Python package exposes the same API via PyO3. Tools are duck-typed objects with `name`, `description`, `arguments_schema` attributes and a `call(args: dict) -> str` method. The `Schema` builder provides a fluent interface for constructing JSON Schemas without writing raw dicts. ### Methods - **SystemLanguageModel()**: Initializes the language model. - **Session(model, instructions, tools)**: Creates a new session. - **respond(prompt, options)**: Sends a prompt and gets a response. - **stream_response(prompt, callback, options)**: Streams response chunks. - **respond_structured(prompt, schema, options)**: Generates structured output. - **ContextLimit.default_on_device()**: Gets default context limit. - **Session.context_usage(limit)**: Gets current context usage. - **compact_transcript(model, transcript_json)**: Compacts a transcript. - **estimate_tokens(text, chars_per_token)**: Estimates token count. ### Parameters - **model**: `SystemLanguageModel` instance. - **instructions**: String for initial assistant instructions. - **tools**: List of tool objects. - **prompt**: The user's input string. - **options**: `GenerationOptions` object. - **callback**: Function to handle streaming chunks. - **schema**: Dictionary representing the desired output schema. - **limit**: `ContextLimit` object. - **transcript_json**: JSON string of the transcript. - **chars_per_token**: Estimated characters per token for estimation. ### Example (Python) ```python import fm # Model and session setup model = fm.SystemLanguageModel() model.ensure_available() # raises typed exception if unavailable session = fm.Session( model, instructions="You are a concise assistant.", ) opts = fm.GenerationOptions(temperature=0.6, max_response_tokens=200) # Basic generation response = session.respond("What is the tallest mountain on Earth?", opts) print(response.content) # "Mount Everest …" print(len(response)) # character count # Streaming chunks = [] session.stream_response( "Count from 1 to 5.", lambda chunk: chunks.append(chunk), opts, ) print("".join(chunks)) # Structured output via Schema builder schema = ( fm.Schema.object() .property("name", fm.Schema.string(), required=True) .property("score", fm.Schema.integer().minimum(0).maximum(100), required=True) .property("tags", fm.Schema.array(fm.Schema.string()).min_items(1)) ) result = session.respond_structured("Generate a quiz result for 'Alice'", schema.to_dict(), opts) print(result) # {'name': 'Alice', 'score': 92, 'tags': ['math', 'science']} # Tool calling with duck-typed tool class EchoTool: name = "echo" description = "Echoes the input message back" arguments_schema = { "type": "object", "properties": {"message": {"type": "string"}}, "required": ["message"], } def call(self, args: dict) -> str: return f"Echo: {args.get('message', '')}" tool_session = fm.Session(model, tools=[EchoTool()]) r = tool_session.respond("Use the echo tool to say 'hello world'") print(r.content) # Context window management limit = fm.ContextLimit.default_on_device() # 4096 tokens, 512 reserved usage = session.context_usage(limit) print(f"{usage.estimated_tokens}/{usage.max_tokens} tokens ({usage.utilization:.1%})") # Compact a long transcript if usage.over_limit: summary = fm.compact_transcript(model, session.transcript_json) print("Summary:", summary) # Token heuristic print(fm.estimate_tokens("Hello world", chars_per_token=4)) # 3 ``` ``` -------------------------------- ### Install fm-rs with pip Source: https://github.com/blacktop/fm-rs/blob/main/bindings/python/README.md Install the fm-rs Python package using pip. Ensure you meet the system requirements before installation. ```bash pip install fm-rs ``` -------------------------------- ### Basic fm Python API Usage Source: https://context7.com/blacktop/fm-rs/llms.txt Illustrates fundamental operations using the fm Python package, including model and session setup, basic response generation, and streaming responses. Shows how to get the content and character count of a response. ```python import fm # Model and session setup model = fm.SystemLanguageModel() model.ensure_available() # raises typed exception if unavailable session = fm.Session( model, instructions="You are a concise assistant.", ) opts = fm.GenerationOptions(temperature=0.6, max_response_tokens=200) # Basic generation response = session.respond("What is the tallest mountain on Earth?", opts) print(response.content) # "Mount Everest …" print(len(response)) # character count ``` ```python # Streaming chunks = [] session.stream_response( "Count from 1 to 5.", lambda chunk: chunks.append(chunk), opts, ) print("".join(chunks)) ``` -------------------------------- ### Python Bindings Development Workflow Source: https://github.com/blacktop/fm-rs/blob/main/bindings/python/README.md Use these commands to set up the development environment, build the Python package, and run tests. ```bash cd bindings/python uv sync uv run maturin develop uv run pytest tests/ ``` -------------------------------- ### Initialize Model and Check Availability (Rust) Source: https://context7.com/blacktop/fm-rs/llms.txt Creates the default language model handle and checks its availability. Use `ensure_available()` for a single call that maps availability to a typed error. Also shows how to estimate token usage for a prompt. ```rust use fm_rs::{SystemLanguageModel, ModelAvailability}; fn main() -> Result<(), fm_rs::Error> { let model = SystemLanguageModel::new()?; // Detailed availability check match model.availability() { ModelAvailability::Available => println!("Ready"), ModelAvailability::DeviceNotEligible => { eprintln!("Device does not support Apple Intelligence"); return Ok(()) } ModelAvailability::AppleIntelligenceNotEnabled => { eprintln!("Enable Apple Intelligence in Settings → Apple Intelligence & Siri"); return Ok(()) } ModelAvailability::ModelNotReady => { eprintln!("Model is still downloading"); return Ok(()) } ModelAvailability::Unknown => { eprintln!("Unknown unavailability reason"); return Ok(()) } } // Or use the convenience method that returns an error immediately model.ensure_available()?; // Token usage estimate for a prompt (uses platform API when available, // otherwise falls back to a characters-per-token heuristic) let usage = model.token_usage_for("Hello, how are you?")?; println!("Estimated tokens: {}", usage.token_count); Ok(()) } ``` -------------------------------- ### Context Window Management with fm-rs Source: https://context7.com/blacktop/fm-rs/llms.txt Shows how to estimate and manage token usage within the context window using `ContextLimit` and `Session.context_usage`. Includes a standalone token estimation helper. ```rust use fm_rs::{ContextLimit, GenerationOptions, Session, SystemLanguageModel, estimate_tokens}; fn main() -> Result<(), fm_rs::Error> { let model = SystemLanguageModel::new()?; model.ensure_available()?; let session = Session::with_instructions(&model, "You are a helpful assistant.")?; let options = GenerationOptions::builder().temperature(0.5).build(); for question in &["Tell me about Tokyo.", "What are the best attractions?", "When to visit?"] { let _ = session.respond(question, &options)?; } // Default on-device limit: 4096 tokens, 512 reserved let limit = ContextLimit::default_on_device(); let usage = session.context_usage(&limit)?; println!("Estimated: {} / {} tokens", usage.estimated_tokens, usage.max_tokens); println!("Available: {} tokens", usage.available_tokens); println!("Utilization: {:.1}%", usage.utilization * 100.0); println!("Over limit: {}", usage.over_limit); // Standalone helper – no session required let tokens = estimate_tokens("Hello, world!", 4); println!("Heuristic estimate: {tokens} tokens"); // Return an error if over budget session.ensure_context_within(&limit)?; Ok(()) } ``` -------------------------------- ### Configuring Generation Options with Builder Source: https://context7.com/blacktop/fm-rs/llms.txt Illustrates using the `GenerationOptions` builder to control sampling strategy, temperature, and token limits. Use `try_temperature` for fallible temperature setting. ```rust use fm_rs::{GenerationOptions, Sampling}; fn main() { // Random sampling at temperature 0.7, capped at 500 tokens let creative = GenerationOptions::builder() .temperature(0.7) .sampling(Sampling::Random) .max_response_tokens(500) .build(); // Deterministic (greedy) output let deterministic = GenerationOptions::builder() .sampling(Sampling::Greedy) .build(); // Default – uses model's built-in sampling settings let default_opts = GenerationOptions::default(); // Fallible temperature setter let result = GenerationOptions::builder() .try_temperature(3.0); // returns Err(Error::InvalidInput(...)) assert!(result.is_err()); println!("creative JSON : {}", creative.to_json()); println!("deterministic : {}", deterministic.to_json()); println!("default : {}", default_opts.to_json()); } ``` -------------------------------- ### SystemLanguageModel::new Source: https://context7.com/blacktop/fm-rs/llms.txt Creates the default on-device language model handle and checks its availability. It returns an error if the FoundationModels.framework is not present. The availability can be checked in detail or with a convenience method that immediately returns an error. ```APIDOC ## `SystemLanguageModel::new` / `SystemLanguageModel()` Creates the default on-device language model handle and checks availability. Returns `Err` (Rust) or raises `ModelNotAvailableError` (Python) if `FoundationModels.framework` is not present. Use `ensure_available()` for a single call that maps the availability code to the appropriate typed error. ```rust use fm_rs::{SystemLanguageModel, ModelAvailability}; fn main() -> Result<(), fm_rs::Error> { let model = SystemLanguageModel::new()?; // Detailed availability check match model.availability() { ModelAvailability::Available => println!("Ready"), ModelAvailability::DeviceNotEligible => { eprintln!("Device does not support Apple Intelligence"); return Ok(()) } ModelAvailability::AppleIntelligenceNotEnabled => { eprintln!("Enable Apple Intelligence in Settings → Apple Intelligence & Siri"); return Ok(()) } ModelAvailability::ModelNotReady => { eprintln!("Model is still downloading"); return Ok(()) } ModelAvailability::Unknown => { eprintln!("Unknown unavailability reason"); return Ok(()) } } // Or use the convenience method that returns an error immediately model.ensure_available()?; // Token usage estimate for a prompt (uses platform API when available, // otherwise falls back to a characters-per-token heuristic) let usage = model.token_usage_for("Hello, how are you?")?; println!("Estimated tokens: {}", usage.token_count); Ok(()) } ``` ``` -------------------------------- ### Compact Session with fm-rs Source: https://context7.com/blacktop/fm-rs/llms.txt Demonstrates compacting a long conversation session using `compact_session_if_needed` to save context. It shows how to handle cases where compaction is needed and when the session is still within budget. ```rust use fm_rs::{ CompactionConfig, ContextLimit, GenerationOptions, Session, SystemLanguageModel, compact_session_if_needed, compact_transcript, }; fn main() -> Result<(), fm_rs::Error> { let model = SystemLanguageModel::new()?; model.ensure_available()?; let session = Session::with_instructions(&model, "You are a helpful assistant.")?; let options = GenerationOptions::builder().temperature(0.5).build(); // Simulate a long conversation let _ = session.respond("Give me an overview of machine learning.", &options)?; let _ = session.respond("What are neural networks?", &options)?; let _ = session.respond("Explain backpropagation.", &options)?; // Use a tight limit to demonstrate compaction let limit = ContextLimit::new(256).with_reserved_response_tokens(64); let config = CompactionConfig::default(); // chunk_tokens=800, max_summary_tokens=400 match compact_session_if_needed( &model, &session, &limit, &config, Some("You are a helpful assistant."), // base instructions for the new session )? { None => println!("Still within budget – no compaction needed."), Some(compacted) => { println!("Compacted summary:\n{}\n", compacted.summary); // Continue the conversation in the compacted session let follow_up = compacted.session.respond( "Given what we discussed, what should I study next?", &options, )?; println!("Follow-up: {}", follow_up.content()); } } // Lower-level: compact a transcript string directly let transcript_json = session.transcript_json()?; let summary = compact_transcript(&model, &transcript_json, &config)?; println!("Direct summary: {summary}"); Ok(()) } ``` -------------------------------- ### Rust API: Session Compaction and Transcript Summarization Source: https://context7.com/blacktop/fm-rs/llms.txt Demonstrates how to use `compact_session_if_needed` to manage session context and `compact_transcript` to summarize a transcript directly in Rust. ```APIDOC ## `compact_session_if_needed` / `compact_transcript` Summarizes a long transcript using the on-device model itself, then seeds a fresh session with the summary. `compact_session_if_needed` combines the budget check and rollover in one call, returning `None` if still within budget or `Some(CompactedSession)` with the new session and generated summary. ### Method Rust Function ### Parameters for `compact_session_if_needed` - **model** (*SystemLanguageModel*) - Reference to the language model. - **session** (*Session*) - The current session to compact. - **limit** (*ContextLimit*) - The context limit configuration. - **config** (*CompactionConfig*) - Configuration for the compaction process. - **base_instructions** (*Option*) - Optional base instructions for the new session. ### Returns for `compact_session_if_needed` - **Result, fm_rs::Error>** - `None` if within budget, `Some(CompactedSession)` if compacted. ### Parameters for `compact_transcript` - **model** (*SystemLanguageModel*) - Reference to the language model. - **transcript_json** (*String*) - JSON string of the transcript to summarize. - **config** (*CompactionConfig*) - Configuration for the compaction process. ### Returns for `compact_transcript` - **Result** - The generated summary string. ### Example (Rust) ```rust use fm_rs::{ CompactionConfig, ContextLimit, GenerationOptions, Session, SystemLanguageModel, compact_session_if_needed, compact_transcript, }; fn main() -> Result<(), fm_rs::Error> { let model = SystemLanguageModel::new()?; model.ensure_available()?; let session = Session::with_instructions(&model, "You are a helpful assistant.")?; let options = GenerationOptions::builder().temperature(0.5).build(); // Simulate a long conversation let _ = session.respond("Give me an overview of machine learning.", &options)?; let _ = session.respond("What are neural networks?", &options)?; let _ = session.respond("Explain backpropagation.", &options)?; // Use a tight limit to demonstrate compaction let limit = ContextLimit::new(256).with_reserved_response_tokens(64); let config = CompactionConfig::default(); // chunk_tokens=800, max_summary_tokens=400 match compact_session_if_needed( &model, &session, &limit, &config, Some("You are a helpful assistant."), // base instructions for the new session )? { None => println!("Still within budget – no compaction needed."), Some(compacted) => { println!("Compacted summary:\n{}\n", compacted.summary); // Continue the conversation in the compacted session let follow_up = compacted.session.respond( "Given what we discussed, what should I study next?", &options, )?; println!("Follow-up: {}", follow_up.content()); } } // Lower-level: compact a transcript string directly let transcript_json = session.transcript_json()?; let summary = compact_transcript(&model, &transcript_json, &config)?; println!("Direct summary: {summary}"); Ok(()) } ``` ``` -------------------------------- ### Session::new / Session::with_instructions / Session(model, ...) Source: https://context7.com/blacktop/fm-rs/llms.txt Creates a conversation session that maintains transcript state between turns. `with_instructions` primes the model with a system-level role description. Sessions are `Send + Sync` in Rust; wrap in `Arc>` when sharing across threads. ```APIDOC ## `Session::new` / `Session::with_instructions` / `Session(model, ...)` Creates a conversation session that maintains transcript state between turns. `with_instructions` primes the model with a system-level role description. In Python the `Session` constructor accepts an optional `instructions` keyword argument. Sessions are `Send + Sync` in Rust; wrap in `Arc>` when sharing across threads. ```rust use fm_rs::{GenerationOptions, Session, SystemLanguageModel}; fn main() -> Result<(), fm_rs::Error> { let model = SystemLanguageModel::new()?; model.ensure_available()?; // Session with a system-level role let session = Session::with_instructions( &model, "You are a concise technical assistant. Answer in at most two sentences." )?; let options = GenerationOptions::builder() .temperature(0.5) .max_response_tokens(200) .build(); // Multi-turn conversation – the session keeps the full transcript let r1 = session.respond("What is ownership in Rust?", &options)?; println!("Turn 1: {}", r1.content()); let r2 = session.respond("Give me a one-line code example.", &options)?; println!("Turn 2: {}", r2.content()); // Persist the conversation for later restoration let json = session.transcript_json()?; println!("Transcript JSON length: {} bytes", json.len()); // Restore the conversation in a new session let restored = Session::from_transcript(&model, &json)?; let r3 = restored.respond("Summarize what we discussed.", &options)?; println!("Restored turn: {}", r3.content()); Ok(()) } ``` ``` -------------------------------- ### Structured Output Generation with Python fm Source: https://context7.com/blacktop/fm-rs/llms.txt Demonstrates generating structured output using the `Schema` builder in the fm Python package. This allows defining JSON schemas fluently for precise data generation. ```python # Structured output via Schema builder schema = ( fm.Schema.object() .property("name", fm.Schema.string(), required=True) .property("score", fm.Schema.integer().minimum(0).maximum(100), required=True) .property("tags", fm.Schema.array(fm.Schema.string()).min_items(1)) ) result = session.respond_structured("Generate a quiz result for 'Alice'", schema.to_dict(), opts) print(result) # {'name': 'Alice', 'score': 92, 'tags': ['math', 'science']} ``` -------------------------------- ### Token Estimation with Python fm Source: https://context7.com/blacktop/fm-rs/llms.txt Demonstrates how to estimate the number of tokens for a given string using the `estimate_tokens` function in the fm Python package. Allows specifying characters per token. ```python # Token heuristic print(fm.estimate_tokens("Hello world", chars_per_token=4)) # 3 ``` -------------------------------- ### Context Window Management and Compaction in Python fm Source: https://context7.com/blacktop/fm-rs/llms.txt Illustrates how to check context window usage and trigger transcript compaction in the fm Python package. It uses `ContextLimit` and checks `usage.over_limit` to decide whether to compact. ```python # Context window management limit = fm.ContextLimit.default_on_device() # 4096 tokens, 512 reserved usage = session.context_usage(limit) print(f"{usage.estimated_tokens}/{usage.max_tokens} tokens ({usage.utilization:.1%})") # Compact a long transcript if usage.over_limit: summary = fm.compact_transcript(model, session.transcript_json) print("Summary:", summary) ``` -------------------------------- ### Generate a full response with Session::respond Source: https://context7.com/blacktop/fm-rs/llms.txt Use `Session::respond` to send a prompt to the model and wait for the complete text response. This method is suitable for scenarios where the entire output is needed before proceeding. Ensure the model is available and sessions are properly initialized. ```rust use fm_rs::{GenerationOptions, Sampling, Session, SystemLanguageModel}; fn main() -> Result<(), fm_rs::Error> { let model = SystemLanguageModel::new()?; model.ensure_available()?; let session = Session::new(&model)?; // Greedy (deterministic) sampling with a hard token cap let options = GenerationOptions::builder() .sampling(Sampling::Greedy) .max_response_tokens(100) .build(); let response = session.respond("List three benefits of Rust.", &options)?; // Response implements Display and AsRef println!("{response}"); assert!(!response.content().is_empty()); // Consume into owned String let text: String = response.into_content(); println!("Length: {} chars", text.len()); Ok(()) } ``` -------------------------------- ### Manage Conversation Session (Rust) Source: https://context7.com/blacktop/fm-rs/llms.txt Creates a conversation session that maintains transcript state. `with_instructions` primes the model with a system-level role. Sessions can be persisted to JSON and restored. Sessions are `Send + Sync` in Rust; wrap in `Arc>` when sharing across threads. ```rust use fm_rs::{GenerationOptions, Session, SystemLanguageModel}; fn main() -> Result<(), fm_rs::Error> { let model = SystemLanguageModel::new()?; model.ensure_available()?; // Session with a system-level role let session = Session::with_instructions( &model, "You are a concise technical assistant. Answer in at most two sentences." )?; let options = GenerationOptions::builder() .temperature(0.5) .max_response_tokens(200) .build(); // Multi-turn conversation – the session keeps the full transcript let r1 = session.respond("What is ownership in Rust?", &options)?; println!("Turn 1: {}", r1.content()); let r2 = session.respond("Give me a one-line code example.", &options)?; println!("Turn 2: {}", r2.content()); // Persist the conversation for later restoration let json = session.transcript_json()?; println!("Transcript JSON length: {} bytes", json.len()); // Restore the conversation in a new session let restored = Session::from_transcript(&model, &json)?; let r3 = restored.respond("Summarize what we discussed.", &options)?; println!("Restored turn: {}", r3.content()); Ok(()) } ``` -------------------------------- ### Context Window Management Source: https://context7.com/blacktop/fm-rs/llms.txt Tools for estimating and managing the context window usage, including context limits and session context usage. ```APIDOC ## Context window management: `ContextLimit`, `ContextUsage`, `session.context_usage` ### Description Estimates how many tokens the current transcript consumes relative to a configured budget. Use `ContextLimit::default_on_device()` for the 4096-token on-device default with 512 tokens reserved for the next response. ### Method `session.context_usage(limit)` and `session.ensure_context_within(limit)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```rust use fm_rs::{ContextLimit, GenerationOptions, Session, SystemLanguageModel, estimate_tokens}; fn main() -> Result<(), fm_rs::Error> { let model = SystemLanguageModel::new()?; model.ensure_available()?; let session = Session::with_instructions(&model, "You are a helpful assistant.")?; let options = GenerationOptions::builder().temperature(0.5).build(); for question in &["Tell me about Tokyo.", "What are the best attractions?", "When to visit?"] { let _ = session.respond(question, &options)?; } // Default on-device limit: 4096 tokens, 512 reserved let limit = ContextLimit::default_on_device(); let usage = session.context_usage(&limit)?; println!("Estimated: {} / {} tokens", usage.estimated_tokens, usage.max_tokens); println!("Available: {} tokens", usage.available_tokens); println!("Utilization: {:.1}%", usage.utilization * 100.0); println!("Over limit: {}", usage.over_limit); // Standalone helper – no session required let tokens = estimate_tokens("Hello, world!", 4); println!("Heuristic estimate: {tokens} tokens"); // Return an error if over budget session.ensure_context_within(&limit)?; Ok(()) } ``` ### Response #### Success Response (200) Provides `ContextUsage` struct containing token estimates and availability. #### Response Example ```json { "estimated_tokens": 1200, "max_tokens": 4096, "available_tokens": 2896, "utilization": 29.3, "over_limit": false } ``` ``` -------------------------------- ### Tool Calling with Duck-Typed Tools in Python fm Source: https://context7.com/blacktop/fm-rs/llms.txt Shows how to integrate custom tools with the fm Python package using duck typing. Tools require `name`, `description`, `arguments_schema` attributes and a `call` method. ```python # Tool calling with duck-typed tool class EchoTool: name = "echo" description = "Echoes the input message back" arguments_schema = { "type": "object", "properties": {"message": {"type": "string"}}, "required": ["message"], } def call(self, args: dict) -> str: return f"Echo: {args.get('message', '')}" tool_session = fm.Session(model, tools=[EchoTool()]) r = tool_session.respond("Use the echo tool to say 'hello world'") print(r.content) ``` -------------------------------- ### Session::respond Source: https://context7.com/blacktop/fm-rs/llms.txt Sends a prompt to the model and waits for the complete response. The response content can be accessed directly or consumed into an owned String. ```APIDOC ## `Session::respond` / `session.respond()` Sends a prompt to the model and blocks until the full response is generated. Returns a `Response` whose `.content()` / `.content` property holds the text. The same method is available in Python as `session.respond(prompt, options=None)`. ### Parameters - `prompt` (string): The input prompt for the model. - `options` (GenerationOptions, optional): Configuration for response generation. ### Returns - `Response`: An object containing the generated text. ### Example ```rust let response = session.respond("List three benefits of Rust.", &options)?; println!("{}", response); assert!(!response.content().is_empty()); let text: String = response.into_content(); ``` ``` -------------------------------- ### Tool Calling for External Functions Source: https://github.com/blacktop/fm-rs/blob/main/bindings/python/README.md Enable the model to call external functions by defining tools with schemas and implementing their `call` methods. The session is initialized with a list of tool instances. ```python import fm class WeatherTool: name = "get_weather" description = "Gets the current weather for a location" arguments_schema = { "type": "object", "properties": { "city": {"type": "string", "description": "The city name"} }, "required": ["city"] } def call(self, args): city = args.get("city", "Unknown") return f"Sunny, 72°F in {city}" model = fm.SystemLanguageModel() session = fm.Session(model, tools=[WeatherTool()]) response = session.respond("What's the weather in Paris?") print(response.content) ``` -------------------------------- ### GenerationOptions Builder Source: https://context7.com/blacktop/fm-rs/llms.txt Configuration options for controlling the generation process, including temperature, sampling strategy, and maximum response tokens. ```APIDOC ## `GenerationOptions` builder ### Description Controls sampling strategy, temperature, and response token budget. All fields are optional; unset fields use model defaults. The builder's `temperature()` silently clamps out-of-range values; use `try_temperature()` for a fallible alternative. ### Method `GenerationOptions::builder()` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```rust use fm_rs::{GenerationOptions, Sampling}; fn main() { // Random sampling at temperature 0.7, capped at 500 tokens let creative = GenerationOptions::builder() .temperature(0.7) .sampling(Sampling::Random) .max_response_tokens(500) .build(); // Deterministic (greedy) output let deterministic = GenerationOptions::builder() .sampling(Sampling::Greedy) .build(); // Default – uses model's built-in sampling settings let default_opts = GenerationOptions::default(); // Fallible temperature setter let result = GenerationOptions::builder() .try_temperature(3.0); // returns Err(Error::InvalidInput(...)) assert!(result.is_err()); println!("creative JSON : {}", creative.to_json()); println!("deterministic : {}", deterministic.to_json()); println!("default : {}", default_opts.to_json()); } ``` ### Response #### Success Response (200) Returns a `GenerationOptions` struct configured with the specified parameters. #### Response Example ```json { "temperature": 0.7, "sampling": "random", "maximumResponseTokens": 500 } ``` ``` -------------------------------- ### Structured Generation with `respond_structured_gen` Source: https://context7.com/blacktop/fm-rs/llms.txt Demonstrates how to generate structured data directly into a Rust struct using the `respond_structured_gen` method. The schema is derived automatically from the struct definition. ```APIDOC ## `respond_structured_gen` ### Description Generates structured data directly into a specified Rust struct. The schema for generation is derived automatically from the struct's definition, leveraging `serde` and `fm_rs` derive macros. ### Method `session.respond_structured_gen::(prompt, options)` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```rust use fm_rs::{Generable, GenerationOptions, Session, SystemLanguageModel}; use serde::Deserialize; #[derive(Debug, Deserialize, Generable)] #[generable(rename_all = "camelCase")] struct PersonProfile { #[generable(description = "The person\'s full name")] full_name: String, #[generable(minimum = 0, maximum = 150)] age: u32, occupation: String, #[generable(min_items = 1, max_items = 5)] hobbies: Vec, } fn main() -> Result<(), Box> { let model = SystemLanguageModel::new()?; model.ensure_available()?; let session = Session::new(&model)?; let options = GenerationOptions::builder().temperature(0.6).build(); let person: PersonProfile = session.respond_structured_gen( "Generate a fictional software engineer profile", &options, )?; println!("Name: {}", person.full_name); println!("Age: {}", person.age); println!("Job: {}", person.occupation); println!("Hobbies: {:?}", person.hobbies); Ok(()) } ``` ### Response #### Success Response (200) Returns an instance of the specified struct `T` populated with generated data. #### Response Example ```json { "fullName": "Jane Doe", "age": 30, "occupation": "Software Engineer", "hobbies": ["reading", "hiking", "coding"] } ``` ``` -------------------------------- ### Configure Swift rpaths in build.rs Source: https://github.com/blacktop/fm-rs/blob/main/README.md This build script configures the necessary Swift rpaths for linking dynamic libraries on macOS. It finds the Swift toolchain and adds its library path to the linker arguments. ```rust use std::process::Command; fn main() { println!("cargo:rustc-link-arg=-Wl,-rpath,/usr/lib/swift"); if let Ok(output) = Command::new("xcrun") .args(["--toolchain", "default", "--find", "swift"]) .output() { let swift_path = String::from_utf8_lossy(&output.stdout).trim().to_string(); if let Some(toolchain) = std::path::Path::new(&swift_path).parent().and_then(|p| p.parent()) { let lib_path = toolchain.join("lib/swift/macosx"); if lib_path.exists() { println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_path.display()); } } } } ``` -------------------------------- ### Generate structured JSON output with Session::respond_json and Session::respond_structured Source: https://context7.com/blacktop/fm-rs/llms.txt Generate output conforming to a JSON schema using `Session::respond_json` for raw JSON or `Session::respond_structured` for deserialized typed output. This is useful for integrating model output directly into application logic. Ensure the schema is correctly defined and the `serde` crate is used for deserialization. ```rust use fm_rs::{GenerationOptions, Session, SystemLanguageModel}; use serde::Deserialize; use serde_json::json; #[derive(Debug, Deserialize)] struct MovieRecommendation { title: String, year: u32, genre: String, reason: String, } fn main() -> Result<(), Box> { let model = SystemLanguageModel::new()?; model.ensure_available()?; let session = Session::new(&model)?; let options = GenerationOptions::builder().temperature(0.7).build(); let schema = json!({ "type": "object", "properties": { "title": { "type": "string" }, "year": { "type": "integer", "minimum": 1900, "maximum": 2025 }, "genre": { "type": "string" }, "reason": { "type": "string" } }, "required": ["title", "year", "genre", "reason"] }); // Typed deserialization let movie: MovieRecommendation = session.respond_structured( "Recommend a classic science fiction film", &schema, &options, )?; println!("{} ({}) – {}: {}", movie.title, movie.year, movie.genre, movie.reason); // Raw JSON string let raw = session.respond_json( "Recommend another classic film", &schema, &options, )?; println!("Raw: {raw}"); Ok(()) } ``` -------------------------------- ### Session::respond_json and Session::respond_structured Source: https://context7.com/blacktop/fm-rs/llms.txt Generates output conforming to a JSON Schema. `respond_json` returns the raw JSON string, while `respond_structured` deserializes it into a typed Rust object. ```APIDOC ## `Session::respond_json` / `Session::respond_structured` / `session.respond_structured()` Instructs the model to produce output that conforms to a JSON Schema. `respond_json` returns the raw JSON string; `respond_structured` additionally deserializes it into a typed Rust value via `serde`. In Python, `session.respond_structured(prompt, schema_dict)` returns a `dict`. ### Parameters - `prompt` (string): The input prompt for the model. - `schema` (serde_json::Value): The JSON schema to conform to. - `options` (GenerationOptions, optional): Configuration for response generation. ### Returns - `Result` for `respond_json` (raw JSON string). - `Result` for `respond_structured` (deserialized typed value). ### Example ```rust // Typed deserialization let movie: MovieRecommendation = session.respond_structured("Recommend a classic science fiction film", &schema, &options)?; println!("{} ({}) – {}: {}", movie.title, movie.year, movie.genre, movie.reason); // Raw JSON string let raw = session.respond_json("Recommend another classic film", &schema, &options)?; println!("Raw: {raw}"); ``` ``` -------------------------------- ### Add fm-rs Dependency Source: https://github.com/blacktop/fm-rs/blob/main/README.md Add the fm-rs crate to your Cargo.toml file. Enable the derive macro feature for compile-time schema generation. ```toml [dependencies] fm-rs = "0.1" ``` ```toml [dependencies] fm-rs = { version = "0.1", features = ["derive"] } ``` -------------------------------- ### Session::respond_with_timeout Source: https://context7.com/blacktop/fm-rs/llms.txt Similar to `respond`, but includes a timeout. If the model does not complete within the specified duration, an error is returned. ```APIDOC ## `Session::respond_with_timeout` Like `respond`, but cancels the request if the model does not finish within the given `Duration`. Returns `Err(Error::Timeout(...))` on expiry. In Python the equivalent is `session.respond_with_timeout(prompt, timeout_secs, options=None)`. ### Parameters - `prompt` (string): The input prompt for the model. - `options` (GenerationOptions, optional): Configuration for response generation. - `timeout` (Duration): The maximum time to wait for a response. ### Returns - `Result`: A `Response` on success, or an `Error` (potentially `Error::Timeout`) on failure. ### Example ```rust match session.respond_with_timeout("Write a 500-word essay about the history of computing.", &options, Duration::from_secs(10)) { Ok(response) => println!("{}", response.content()), Err(fm_rs::Error::Timeout(msg)) => { eprintln!("Generation timed out: {msg}"); session.cancel(); } Err(e) => return Err(e), } ``` ``` -------------------------------- ### Structured Generation with fm-rs Source: https://context7.com/blacktop/fm-rs/llms.txt Demonstrates generating structured data (PersonProfile) using fm-rs. Requires `derive` feature for fm-rs and serde. The `#[generable]` attribute customizes schema generation. ```rust use fm_rs::{Generable, GenerationOptions, Session, SystemLanguageModel}; use serde::Deserialize; /// A person's profile extracted from text. #[derive(Debug, Deserialize, Generable)] #[generable(rename_all = "camelCase")] struct PersonProfile { /// Full name of the person #[generable(description = "The person's full name")] full_name: String, #[generable(minimum = 0, maximum = 150)] age: u32, occupation: String, #[generable(min_items = 1, max_items = 5)] hobbies: Vec, } fn main() -> Result<(), Box> { let model = SystemLanguageModel::new()?; model.ensure_available()?; let session = Session::new(&model)?; let options = GenerationOptions::builder().temperature(0.6).build(); // Schema is derived automatically; no json!() macro needed let person: PersonProfile = session.respond_structured_gen::( "Generate a fictional software engineer profile", &options, )?; println!("Name: {}", person.full_name); println!("Age: {}", person.age); println!("Job: {}", person.occupation); println!("Hobbies: {:?}", person.hobbies); // Inspect the generated schema println!("{}", serde_json::to_string_pretty(&PersonProfile::schema())?); Ok(()) } ``` -------------------------------- ### Context Management and Usage Source: https://github.com/blacktop/fm-rs/blob/main/bindings/python/README.md Monitor token usage within the conversation context and optionally compact the transcript to save tokens when nearing the limit. ```python import fm model = fm.SystemLanguageModel() session = fm.Session(model) # After some conversation... limit = fm.ContextLimit.default_on_device() usage = session.context_usage(limit) print(f"Tokens used: {usage.estimated_tokens}/{usage.max_tokens}") print(f"Utilization: {usage.utilization:.1%}") if usage.over_limit: # Compact the conversation transcript = session.transcript_json summary = fm.compact_transcript(model, transcript) print(f"Summary: {summary}") ``` -------------------------------- ### Error Handling for Model Availability Source: https://github.com/blacktop/fm-rs/blob/main/bindings/python/README.md Handle potential errors during model initialization and availability checks, such as device ineligibility or Apple Intelligence not being enabled. ```python import fm try: model = fm.SystemLanguageModel() model.ensure_available() except fm.DeviceNotEligibleError: print("This device doesn't support Apple Intelligence") except fm.AppleIntelligenceNotEnabledError: print("Please enable Apple Intelligence in Settings") except fm.ModelNotReadyError: print("Model is still downloading, try again later") except fm.ModelNotAvailableError: print("Model not available for unknown reason") ``` -------------------------------- ### Implement Custom Tools for Model Interaction in Rust Source: https://context7.com/blacktop/fm-rs/llms.txt Defines custom tools the model can invoke during generation by implementing the `Tool` trait. Register tools when creating a session using `Session::with_instructions_and_tools`. ```rust use fm_rs::{ Error, GenerationOptions, Result, Session, SystemLanguageModel, Tool, ToolOutput, }; use serde_json::{json, Value}; use std::sync::Arc; struct CalculatorTool; impl Tool for CalculatorTool { fn name(&self) -> &str { "calculator" } fn description(&self) -> &str { "Performs basic arithmetic. Supports add, subtract, multiply, divide." } fn arguments_schema(&self) -> Value { json!({ "type": "object", "properties": { "operation": { "type": "string", "enum": ["add", "subtract", "multiply", "divide"] }, "a": { "type": "number" }, "b": { "type": "number" } }, "required": ["operation", "a", "b"] }) } fn call(&self, args: Value) -> Result { let op = args["operation"].as_str().unwrap_or("add"); let a = args["a"].as_f64().unwrap_or(0.0); let b = args["b"].as_f64().unwrap_or(0.0); let result = match op { "add" => a + b, "subtract" => a - b, "multiply" => a * b, "divide" => { if b == 0.0 { return Ok(ToolOutput::new("Error: division by zero")); } a / b } other => return Err(Error::InvalidInput(format!("Unknown op: {other}"))), }; Ok(ToolOutput::new(format!( "{result}"))) } } fn main() -> Result<()> { let model = SystemLanguageModel::new()?; model.ensure_available()?; let tools: Vec> = vec![Arc::new(CalculatorTool)]; let session = Session::with_instructions_and_tools( &model, "You are a math assistant. Always use the calculator tool for arithmetic.", &tools, )?; let options = GenerationOptions::builder().temperature(0.3).build(); let response = session.respond("What is 42 multiplied by 17?", &options)?; println!( "{}", response.content()); // Expected: the model calls calculator(operation="multiply", a=42, b=17) → "714" Ok(()) } ```