Skip to content

Openheim lib#22

Merged
themartto merged 5 commits into
devfrom
openheim-lib
May 12, 2026
Merged

Openheim lib#22
themartto merged 5 commits into
devfrom
openheim-lib

Conversation

@themartto
Copy link
Copy Markdown
Contributor

@themartto themartto commented May 12, 2026

Summary

  • New Features

    • Openheim is now embeddable as a Rust library with a public client API.
    • Added configurable session management (create, list, load, delete sessions).
    • Added support for conversation deletion.
    • Added configuration loading from custom file paths.
  • Documentation

    • Added comprehensive library documentation with usage examples, configuration options, and API reference.

@themartto
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

📝 Walkthrough

Walkthrough

This PR transforms Openheim from an internal CLI tool into an embeddable Rust library by refactoring ACP session operations into reusable methods and introducing a client facade with flexible configuration, session management, and RAG access patterns. Library documentation guides users through setup and common usage scenarios.

Changes

Library Client API & Session Lifecycle Refactoring

Layer / File(s) Summary
Build Configuration
Cargo.toml
Explicit lib and bin target definitions specify openheim crate name with src/lib.rs and src/main.rs entry points.
ACP Session Lifecycle Refactoring
src/acp/mod.rs
AgentState gains four session-scoped methods: acp_new_session (create/store session), acp_prompt (stream responses with history), acp_list_sessions (list sessions by cwd), and acp_load_session (resume with history replay). Request handlers delegate to these methods; imports reorganized to use local Result alias.
Session Deletion Support
src/rag/history.rs
HistoryManager::delete_conversation removes conversation JSON files from storage.
Configuration Loading Helper
src/config/mod.rs
New load_config_from loads AppConfig from an arbitrary path with error handling for missing/unparseable files.
Client Facade API
src/client.rs
New module introduces OpenheimClient (main entry point), SessionBuilder (session configuration), SessionHandle (active session), and OpenheimBuilder (dual-mode construction: programmatic provider/key/model/limits or file-based config path). Builder resolves provider-specific API base/model defaults, merges MCP servers, and constructs AgentState. Client methods validate session IDs as UUIDs and expose session/conversation/RAG/introspection APIs.
Public API Surface Expansion
src/lib.rs
New client module added; config re-exports widened to McpServerConfig, ModelsInfo; rag re-exports include Conversation, ConversationMeta, HistoryManager; client facade types (OpenheimClient, SessionBuilder, SessionHandle, OpenheimBuilder) and ACP schema types (SessionUpdate, ContentBlock, SessionInfo, tool-call variants as AcpToolCall) re-exported.
Library Usage Documentation
docs/library.md
Comprehensive guide covering dependency setup, three initialization patterns (default config, custom path, programmatic), session creation/streaming, multi-turn prompts, session management (list/load/delete), RAG history/skills access, tool/server/model introspection, full MCP/history example, ACP event reference, and error/retry semantics.

Sequence Diagrams

sequenceDiagram
  participant User
  participant OpenheimBuilder
  participant AgentState
  participant RagContext
  participant HistoryManager
  User->>OpenheimBuilder: provider/api_key/model/mcp_server
  OpenheimBuilder->>OpenheimBuilder: resolve defaults, load/merge config
  OpenheimBuilder->>RagContext: initialize
  OpenheimBuilder->>AgentState: construct with config
  OpenheimBuilder->>User: return OpenheimClient
  User->>OpenheimClient: new_session().model(m).skills(s).start()
  OpenheimClient->>AgentState: acp_new_session
  AgentState->>HistoryManager: create session
  AgentState->>User: return SessionHandle
  User->>SessionHandle: prompt(text, |update| callback)
  SessionHandle->>AgentState: acp_prompt
  AgentState->>HistoryManager: prepare conversation
  AgentState->>AgentState: run_agent_streaming
  AgentState->>User: emit SessionUpdate via callback
  AgentState->>HistoryManager: save history
Loading
sequenceDiagram
  participant User
  participant OpenheimClient
  participant HistoryManager
  User->>OpenheimClient: load_session(session_id, |update| callback)
  OpenheimClient->>HistoryManager: load conversation history
  HistoryManager->>User: replay user/assistant messages as SessionUpdate
  User->>OpenheimClient: return SessionHandle
  User->>OpenheimClient: delete_session(session_id)
  OpenheimClient->>HistoryManager: delete conversation file
  HistoryManager->>User: return success
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

The PR introduces substantial new public API surface (OpenheimClient, builder, session types) and refactors the ACP module with session lifecycle methods, but changes are cohesive and follow clear patterns. Review focuses on ACP refactoring correctness, client API ergonomics, configuration merging logic, and documentation completeness. Mixed complexity: new client facade is straightforward builder/delegation patterns, ACP refactoring involves streaming logic and session state management, and config/re-exports are lower density.

Possibly related PRs

  • weirdstuff-dev/openheim#18: Modifies MCP-related public API and configuration surface; both PRs update McpServerConfig re-exports and client/builder MCP server handling.
  • weirdstuff-dev/openheim#20: Both add/expose ModelsInfo and McpServerConfig; both surface McpServerStatus via AgentState/client APIs for introspection.

Poem

🐰 Hops through the crate to craft a library,
Building facades where sessions bloom free,
Config flows both paths—file and code alike,
RAG and history, streaming delight!
OpenheimClient hears: "Let embedders thrive!" 📚

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Openheim lib' is vague and generic, using non-descriptive language that fails to convey the specific scope or nature of the changes. Use a more descriptive title that clearly indicates the main objective, such as 'Add public library API for embedding Openheim' or 'Expose Openheim as a Rust library with client facade'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
src/client.rs (3)

75-79: 💤 Low value

UUID parsing error loses specificity.

The UUID parse error is converted to a generic Error::Other with message "invalid session id". For a library API, preserving the original parse error would help callers distinguish between malformed UUIDs and other failure modes.

Consider wrapping the uuid::Error in a dedicated Error::InvalidSessionId(uuid::Error) variant.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client.rs` around lines 75 - 79, The UUID parsing in get_session
currently maps all parse failures to a generic crate::error::Error::Other which
loses the original uuid::Error; change the error handling so
Uuid::parse_str(session_id).map_err(|e|
crate::error::Error::InvalidSessionId(e))? (or add a new
Error::InvalidSessionId(uuid::Error) variant if it doesn't exist) so callers can
distinguish malformed UUIDs from other failures, leaving the subsequent call to
self.state.rag.history.load_conversation(&uuid) unchanged.

45-52: ⚡ Quick win

Current directory fallback may be surprising.

Line 50 falls back to "/" if current_dir() fails, which could happen in containerized environments or when the original directory is deleted. This default may be unexpected for library users and could cause sessions to be incorrectly grouped.

Consider either:

  • Documenting this fallback behavior clearly
  • Requiring cwd to be set explicitly (no default)
  • Using a sentinel value that's more obviously a fallback (e.g., PathBuf::from(""))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client.rs` around lines 45 - 52, The new_session method currently sets
SessionBuilder.cwd to std::env::current_dir().unwrap_or_else(|_|
PathBuf::from("/")), which silently falls back to "/" on error; change this to a
safer behavior: either (a) remove the implicit fallback and return a Result from
new_session or require callers to set cwd (i.e., propagate the current_dir()
error instead of unwrap_or_else), or (b) use a clear sentinel like
PathBuf::from("") and document that SessionBuilder.cwd may be empty when
current_dir() fails; update new_session (and the SessionBuilder type docs) and
any call sites that assume a valid cwd accordingly (refer to new_session,
SessionBuilder, and the cwd field).

304-352: ⚡ Quick win

Empty API key default may cause confusing errors.

Line 316 uses api_key.unwrap_or_default(), which creates an empty string when no key is provided. This will cause authentication failures with cryptic error messages from the LLM provider rather than a clear "API key required" error at build time.

Consider validating that api_key is present and non-empty when building programmatic config, or document that an empty key is allowed for providers that don't require authentication.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client.rs` around lines 304 - 352, The build_programmatic function
currently uses api_key.unwrap_or_default() which silently produces an empty
string and leads to cryptic auth errors; change build_programmatic to validate
the incoming api_key Option (ensure it's Some and not an empty string) and
surface a clear error instead of defaulting: update the function signature to
return Result<(AgentConfig, AppConfig), Error> (or an appropriate custom error),
if api_key is required for the chosen provider return Err with a descriptive
message like "API key required for provider <provider>" otherwise proceed to
populate ProviderConfig and AgentConfig with the validated api_key; reference
build_programmatic, ProviderConfig, and AgentConfig when making the change.
src/acp/mod.rs (1)

163-168: ⚖️ Poor tradeoff

Consider surfacing conversation save failures.

The conversation save failure is logged but execution continues successfully. In a library context, callers may want to know if history persistence failed so they can retry or alert the user.

Consider either:

  • Returning the save error (breaking change to error semantics)
  • Exposing a "partial success" indicator in the response
  • Documenting this behavior clearly in the API docs
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/acp/mod.rs` around lines 163 - 168, The current code swallows errors from
self.rag.history.save_conversation(&conversation) (logged only) so callers can't
detect persistence failures; change the API to surface partial-save state by
extending the function's return payload to include a "history_saved: bool" (or a
richer enum like HistorySaveStatus) and set it to false when save_conversation
returns Err(e) while still returning the existing run_result (use the existing
run_result.map(|_| response_with_history_saved_flag)). Update the function's
response type and any callers of this function accordingly so consumers can
retry or alert when history_saved is false; keep the tracing::warn but also set
history_saved=false when the save errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/acp/mod.rs`:
- Around line 163-168: The current code swallows errors from
self.rag.history.save_conversation(&conversation) (logged only) so callers can't
detect persistence failures; change the API to surface partial-save state by
extending the function's return payload to include a "history_saved: bool" (or a
richer enum like HistorySaveStatus) and set it to false when save_conversation
returns Err(e) while still returning the existing run_result (use the existing
run_result.map(|_| response_with_history_saved_flag)). Update the function's
response type and any callers of this function accordingly so consumers can
retry or alert when history_saved is false; keep the tracing::warn but also set
history_saved=false when the save errors.

In `@src/client.rs`:
- Around line 75-79: The UUID parsing in get_session currently maps all parse
failures to a generic crate::error::Error::Other which loses the original
uuid::Error; change the error handling so
Uuid::parse_str(session_id).map_err(|e|
crate::error::Error::InvalidSessionId(e))? (or add a new
Error::InvalidSessionId(uuid::Error) variant if it doesn't exist) so callers can
distinguish malformed UUIDs from other failures, leaving the subsequent call to
self.state.rag.history.load_conversation(&uuid) unchanged.
- Around line 45-52: The new_session method currently sets SessionBuilder.cwd to
std::env::current_dir().unwrap_or_else(|_| PathBuf::from("/")), which silently
falls back to "/" on error; change this to a safer behavior: either (a) remove
the implicit fallback and return a Result from new_session or require callers to
set cwd (i.e., propagate the current_dir() error instead of unwrap_or_else), or
(b) use a clear sentinel like PathBuf::from("") and document that
SessionBuilder.cwd may be empty when current_dir() fails; update new_session
(and the SessionBuilder type docs) and any call sites that assume a valid cwd
accordingly (refer to new_session, SessionBuilder, and the cwd field).
- Around line 304-352: The build_programmatic function currently uses
api_key.unwrap_or_default() which silently produces an empty string and leads to
cryptic auth errors; change build_programmatic to validate the incoming api_key
Option (ensure it's Some and not an empty string) and surface a clear error
instead of defaulting: update the function signature to return
Result<(AgentConfig, AppConfig), Error> (or an appropriate custom error), if
api_key is required for the chosen provider return Err with a descriptive
message like "API key required for provider <provider>" otherwise proceed to
populate ProviderConfig and AgentConfig with the validated api_key; reference
build_programmatic, ProviderConfig, and AgentConfig when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 61f57dc4-5944-4749-9665-30fd71c0c0e2

📥 Commits

Reviewing files that changed from the base of the PR and between 5e34944 and ecbc8eb.

📒 Files selected for processing (7)
  • Cargo.toml
  • docs/library.md
  • src/acp/mod.rs
  • src/client.rs
  • src/config/mod.rs
  • src/lib.rs
  • src/rag/history.rs

@themartto themartto marked this pull request as ready for review May 12, 2026 19:56
@themartto themartto merged commit 68910a7 into dev May 12, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant