From dfccbf29f18d258602d88b597f6435a66f072f88 Mon Sep 17 00:00:00 2001 From: DK09876 Date: Mon, 15 Dec 2025 02:42:19 -0700 Subject: [PATCH] Added hindsight_liteLLM implementation (#17) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added hindsight_liteLLM implementation * Add instructions for entity vs bank id * Add another line about entity * Address PR review comments and enhance litellm integration - Remove deprecated limit parameter from recall() and arecall() functions since Hindsight uses budget/max_tokens for result control - Remove dead MODEL_MAX_OUTPUT_TOKENS dict and max_output_tokens property from LLMProvider (superseded by hardcoded max_completion_tokens) - Add test-litellm-integration job to CI workflow - Add reflect API support with use_reflect config option - Add verbose mode debug info via get_last_injection_debug() - Add entity_id support for multi-user memory isolation - Add retain() and reflect() wrapper functions - Update docstrings and examples 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 * Make max_memories optional to allow unlimited memory injection - Change max_memories default from 10 to None (no limit) - When max_memories is None, all results from the API are used - Fix recall result handling to properly detect list vs object return - Update wrappers (OpenAI, Anthropic) with same optional behavior This allows users to control memory limits via max_memory_tokens and recall_budget without an artificial count limit. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 * Remove entity_id from hindsight_litellm; add gpt-4o token cap Multi-user support now uses separate bank_ids per user instead of entity_id scoping (e.g., bank_id=f"user-{user_id}"). This simplifies the API and aligns with the Hindsight architecture. Also fixes max_completion_tokens error for gpt-4o models by capping the value at 16384 (gpt-4o's limit) instead of sending the default 65000 which exceeds the model's supported maximum. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 * Fix dark mode styling across Control Plane UI components Improvements to ensure proper text visibility and contrast in both light and dark modes: - Add global CSS rules for datetime-local calendar picker icon visibility using filter: invert() for both light (0.5) and dark (1) modes - Fix text colors in dialog components to use theme-aware foreground colors - Update memory detail panel, document/chunk modals, and data views to use proper dark mode text classes (text-foreground, text-card-foreground) - Fix form labels, headings, and content text in bank selector dialogs - Update entities view and documents view table styling for dark mode - Bump package versions to 0.1.4 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 * Remove session_id feature and add How It Works section to README - Remove session_id and session management (new_session, set_session, get_session) from config.py, callbacks.py, and __init__.py - Session management was a client-only abstraction not backed by core API - Add "How It Works" section to README with visual flow diagram - Update README to remove session management documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 * Fix readme example * Add dark mode again --------- Co-authored-by: Claude Opus 4.5 --- .github/workflows/test.yml | 29 + .../hindsight_api/engine/llm_wrapper.py | 6 +- hindsight-api/hindsight_api/pg0.py | 10 +- hindsight-control-plane/src/app/globals.css | 9 + .../src/components/bank-profile-view.tsx | 4 +- .../src/components/bank-selector.tsx | 15 +- .../src/components/data-view.tsx | 24 +- .../src/components/document-chunk-modal.tsx | 22 +- .../src/components/documents-view.tsx | 28 +- .../src/components/entities-view.tsx | 16 +- .../src/components/memory-detail-panel.tsx | 18 +- .../src/components/ui/dialog.tsx | 4 +- hindsight-integrations/litellm/README.md | 437 +++++++ .../litellm/hindsight_litellm/__init__.py | 817 ++++++++++++++ .../litellm/hindsight_litellm/callbacks.py | 640 +++++++++++ .../litellm/hindsight_litellm/config.py | 232 ++++ .../litellm/hindsight_litellm/wrappers.py | 1000 +++++++++++++++++ hindsight-integrations/litellm/pyproject.toml | 59 + .../litellm/tests/__init__.py | 1 + .../litellm/tests/test_integration.py | 471 ++++++++ 20 files changed, 3772 insertions(+), 70 deletions(-) create mode 100644 hindsight-integrations/litellm/README.md create mode 100644 hindsight-integrations/litellm/hindsight_litellm/__init__.py create mode 100644 hindsight-integrations/litellm/hindsight_litellm/callbacks.py create mode 100644 hindsight-integrations/litellm/hindsight_litellm/config.py create mode 100644 hindsight-integrations/litellm/hindsight_litellm/wrappers.py create mode 100644 hindsight-integrations/litellm/pyproject.toml create mode 100644 hindsight-integrations/litellm/tests/__init__.py create mode 100644 hindsight-integrations/litellm/tests/test_integration.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 831b2626..6947fe3a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -441,3 +441,32 @@ jobs: run: | echo "=== API Server Logs ===" cat /tmp/api-server.log || echo "No API server log found" + + test-litellm-integration: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + prune-cache: false + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version-file: ".python-version" + + - name: Build litellm integration + working-directory: ./hindsight-integrations/litellm + run: uv build + + - name: Install dependencies + working-directory: ./hindsight-integrations/litellm + run: uv sync --extra dev + + - name: Run tests + working-directory: ./hindsight-integrations/litellm + run: uv run pytest tests -v \ No newline at end of file diff --git a/hindsight-api/hindsight_api/engine/llm_wrapper.py b/hindsight-api/hindsight_api/engine/llm_wrapper.py index d3ff78ed..4d87c33f 100644 --- a/hindsight-api/hindsight_api/engine/llm_wrapper.py +++ b/hindsight-api/hindsight_api/engine/llm_wrapper.py @@ -175,9 +175,13 @@ class LLMProvider: is_reasoning_model = any(x in model_lower for x in ["gpt-5", "o1", "o3"]) # For GPT-4 and GPT-4.1 models, cap max_completion_tokens to 32000 + # For GPT-4o models, cap to 16384 is_gpt4_model = any(x in model_lower for x in ["gpt-4.1", "gpt-4-"]) + is_gpt4o_model = "gpt-4o" in model_lower if max_completion_tokens is not None: - if is_gpt4_model and max_completion_tokens > 32000: + if is_gpt4o_model and max_completion_tokens > 16384: + max_completion_tokens = 16384 + elif is_gpt4_model and max_completion_tokens > 32000: max_completion_tokens = 32000 # For reasoning models, max_completion_tokens includes reasoning + output tokens # Enforce minimum of 16000 to ensure enough space for both diff --git a/hindsight-api/hindsight_api/pg0.py b/hindsight-api/hindsight_api/pg0.py index ac642b52..1dc7ff6e 100644 --- a/hindsight-api/hindsight_api/pg0.py +++ b/hindsight-api/hindsight_api/pg0.py @@ -54,7 +54,9 @@ class EmbeddedPostgres: loop = asyncio.get_event_loop() info = await loop.run_in_executor(None, pg0.start) logger.info(f"PostgreSQL started on port {self.port}") - return info.uri + # Construct URI manually since pg0-embedded may return None + uri = info.uri if info and info.uri else f"postgresql://{self.username}:{self.password}@localhost:{self.port}/{self.database}" + return uri except Exception as e: last_error = str(e) if attempt < max_retries: @@ -89,9 +91,9 @@ class EmbeddedPostgres: pg0 = self._get_pg0() loop = asyncio.get_event_loop() info = await loop.run_in_executor(None, pg0.info) - if info is None or not info.running: - raise RuntimeError("PostgreSQL server is not running or URI not available") - return info.uri + # Construct URI manually since pg0-embedded may return None + uri = info.uri if info and info.uri else f"postgresql://{self.username}:{self.password}@localhost:{self.port}/{self.database}" + return uri async def is_running(self) -> bool: """Check if the PostgreSQL server is currently running.""" diff --git a/hindsight-control-plane/src/app/globals.css b/hindsight-control-plane/src/app/globals.css index e41afbd5..b58199f1 100644 --- a/hindsight-control-plane/src/app/globals.css +++ b/hindsight-control-plane/src/app/globals.css @@ -180,4 +180,13 @@ code, pre { -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; +} + +/* Fix datetime-local calendar icon visibility in both light and dark modes */ +input[type="datetime-local"]::-webkit-calendar-picker-indicator { + filter: invert(0.5); +} + +.dark input[type="datetime-local"]::-webkit-calendar-picker-indicator { + filter: invert(1); } \ No newline at end of file diff --git a/hindsight-control-plane/src/components/bank-profile-view.tsx b/hindsight-control-plane/src/components/bank-profile-view.tsx index 7a4896ce..aa823d86 100644 --- a/hindsight-control-plane/src/components/bank-profile-view.tsx +++ b/hindsight-control-plane/src/components/bank-profile-view.tsx @@ -239,7 +239,7 @@ export function BankProfileView() {
{editMode ? ( <> - diff --git a/hindsight-control-plane/src/components/bank-selector.tsx b/hindsight-control-plane/src/components/bank-selector.tsx index 76b3aa60..8ff4573d 100644 --- a/hindsight-control-plane/src/components/bank-selector.tsx +++ b/hindsight-control-plane/src/components/bank-selector.tsx @@ -266,7 +266,7 @@ function BankSelectorInner() {