The OllamaEmbeddingModel was ignoring the base_url from credentials/config,
always falling back to env vars or localhost. This caused embedding failures
for users with custom Ollama endpoints.
Fixes#655
The language field on EpisodeProfile was being saved to the database but
had no effect during generation because podcast-creator 0.11.x didn't
support the language parameter. Version 0.12.0 adds language support to
the generation pipeline (outline + transcript templates), and since
open-notebook already passes the full episode profile config to
podcast-creator, the language field is picked up automatically.
Closes#640
* feat(podcasts): integrate model registry for profiles and credential passthrough
Replace loose provider/model string fields with record<model> references
in podcast profiles, enabling credential passthrough to podcast-creator.
Backend:
- EpisodeProfile: outline_llm, transcript_llm (record<model>) replace
outline_provider/outline_model strings. New language field (BCP 47).
- SpeakerProfile: voice_model (record<model>) replaces tts_provider/
tts_model strings. Per-speaker voice_model override support.
- Migration 14: schema changes making legacy fields optional, adding new
record<model> fields.
- Data migration (migration.py): auto-converts legacy profiles to model
registry references on startup. Idempotent.
- podcast_commands.py: resolves credentials for ALL profiles before
calling podcast-creator.
- New /api/languages endpoint (pycountry + babel) with BCP 47 locale
codes (pt-BR, en-US, etc.).
Frontend:
- Episode/speaker profile forms use ModelSelector instead of manual
provider/model dropdowns.
- Language dropdown with BCP 47 codes in episode profile form.
- Per-speaker TTS voice model override in speaker profile form.
- "Templates" tab renamed to "Profiles".
- Setup required badge on unconfigured profiles.
- i18n updated across all 8 locales.
Closes#486, closes#552
* fix(i18n): remove unused legacy podcast provider/model keys
Remove 10 orphaned i18n keys across all 8 locales that were left behind
after replacing manual provider/model dropdowns with ModelSelector.
* fix: address review violations in podcast model registry
- P1: Remove profiles with failed model resolution from dicts to prevent
podcast-creator validation errors on unrelated profiles
- P2: Use centralized QUERY_KEYS.languages instead of inline key
- P3: Fix ISO 639-1 → BCP 47 in model field description and CLAUDE.md
- P3: Update "templates" → "profiles" in locale string values (all 8)
* chore: bump version to 1.8.0
- Add batching to generate_embeddings() (50 texts per batch with per-batch retry)
to prevent 413 Payload Too Large errors on large documents
- Add 413 error classification rule for user-friendly error messages
- Fix misleading "Created 0 embedded chunks" log in process_source_command
by removing premature get_embedded_chunks() call (embedding is fire-and-forget)
Closes#594
* docs: update CLAUDE.md and user docs for error handling and podcast retry
Add missing documentation for features introduced in v1.7.2 (#590) and
v1.7.3 (#595): error classification system, global exception handlers,
ConfigurationError, podcast failure recovery, and retry endpoint.
* chore: update uv.lock
* fix: surface podcast errors and enable retry for failed episodes
Fixes#335, #300
Re-raise exceptions in podcast command so surreal-commands marks jobs as
failed instead of completed. Surface error_message in API responses and
add a retry endpoint that deletes the failed episode and re-submits the
generation job. Frontend shows error details on failed episodes with a
retry button. Translations added for all 8 locales.
* fix: bump podcast-creator to >= 0.10
Fixes#302
* chore: release 1.7.3 - podcast failure recovery and retry
Bump podcast-creator to >= 0.11.2, disable automatic retries for
podcast generation to prevent duplicate episodes, and bump version
to 1.7.3.
Fixes#211, #218, #185, #355, #300, #302
* fix: resolve TypeScript error in handleRetry return type
- Catch ConfigurationError alongside ValueError in get_default_model()
to preserve graceful fallback after ValueError→ConfigurationError migration
- Add _truncate() helper to error_classifier to cap pass-through and
default error messages at 200 chars, avoiding verbose internal details
Esperanto's AIFactory.create_text_to_speech() did not accept a config
dict like the other factory methods, so credentials configured via the
UI were not passed through. Fixed upstream in esperanto 2.9.2.
Refs #571
* feat: replace provider config with credential-based system (#477)
Introduce a new credential management system replacing the old
ProviderConfig singleton and standalone Models page. Each credential
stores encrypted API keys and provider-specific configuration with
full CRUD support via a unified settings UI.
Backend:
- Add Credential domain model with encrypted API key storage
- Add credentials API router (CRUD, discovery, registration, testing)
- Add encryption utilities for secure key storage
- Add key_provider for DB-first env-var fallback provisioning
- Add connection tester and model discovery services
- Integrate ModelManager with credential-based config
- Add provider name normalization for Esperanto compatibility
- Add database migrations 11-12 for credential schema
Frontend:
- Rewrite settings/api-keys page with credential management UI
- Add model discovery dialog with search and custom model support
- Add compact default model assignments (primary/advanced layout)
- Add inline model testing and credential connection testing
- Add env-var migration banner
- Update navigation to unified settings page
- Remove standalone models page and old settings components
i18n:
- Update all 7 locale files with credential and model management keys
Closes#477
Co-Authored-By: JFMD <git@jfmd.us>
Co-Authored-By: OraCatQAQ <570768706@qq.com>
* fix: address PR #540 review comments
- Fix docs referencing removed Models page
- Fix error-handler returning raw messages instead of i18n keys
- Fix auth.py misleading docstring and missing no-password guard
- Fix connection_tester using wrong env var for openai_compatible
- Add provision_provider_keys before model discovery/sync
- Update CLAUDE.md to reflect credential-based system
- Fix missing closing brace in api-keys page useEffect
* fix: add logging to credential migration and surface errors in UI
- Add comprehensive logging to migrate-from-env and
migrate-from-provider-config endpoints (start, per-provider
progress, success/failure with stack traces, final summary)
- Fix frontend migration hooks ignoring errors array from response
- Show error toast when migration fails instead of "nothing to migrate"
- Invalidate status/envStatus queries after migration so banner updates
* docs: update CLAUDE.md files for credential system
Replace stale ProviderConfig and /api-keys/ references across 8 CLAUDE.md
files to reflect the new Credential-based system from PR #540.
* docs: update user documentation for credential-based system
Replace env var API key instructions with Settings UI credential
workflow across all user-facing documentation. The new flow is:
set OPEN_NOTEBOOK_ENCRYPTION_KEY → start services → add credential
in Settings UI → test → discover models → register.
- Rewrite ai-providers.md, api-configuration.md, environment-reference.md
- Update all quick-start guides and installation docs
- Update ollama.md, openai-compatible.md, local-tts/stt networking sections
- Update reverse-proxy.md, development-setup.md, security.md
- Fix broken links to non-existent docs/deployment/ paths
- Add credentials endpoints to api-reference.md
- Move all API key env vars to deprecated/legacy sections
* chore: bump version to 1.7.0-rc1
Release candidate for credential-based provider management system.
* fix: initialize provider before try block in test_credential
Prevents UnboundLocalError when Credential.get() throws (e.g.,
invalid credential_id) before provider is assigned.
* fix: reorder down migration to drop index before table
Removes duplicate REMOVE FIELD statement and reorders so the index
is dropped before the table, preventing rollback failures.
* refactor: simplify encryption key to always derive via SHA-256
Remove the dual code path in _ensure_fernet_key() that detected native
Fernet keys. Since the credential system is new, always deriving via
SHA-256 removes unnecessary complexity. Also removes the generate_key()
function and Fernet.generate_key() references from docs.
* fix: correct mock patch targets in embedding tests and URL validation
Fix embedding tests patching wrong module path for model_manager
(was targeting open_notebook.utils.embedding.model_manager but it's
imported locally from open_notebook.ai.models). Also fix URL validation
to allow unresolvable hostnames since they may be valid in the
deployment environment (e.g., Azure endpoints, internal DNS).
* feat: add global setup banner for encryption and migration status
Show a persistent banner in AppShell when encryption key is missing
(red) or env var API keys can be migrated (amber), so users see
these prompts on every page instead of only on Settings > API Keys.
Includes a docs link for the encryption banner and i18n support
across all 7 locales.
* docs: several improvements to docker-compose e env examples
* Update README.md
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* docs: fix env var format in README and update model setup instructions
Align the encryption key snippet in README Step 2 with the list
format used in the compose file. Replace deprecated "Settings →
Models" instructions with credential-based Discover Models flow.
* fix: address credential system review issues
- Fix SSRF bypass via IPv4-mapped IPv6 addresses (::ffff:169.254.x.x)
- Fix TTS connection test missing config parameter
- Add Azure-specific model discovery using api-key auth header
- Add Vertex static model list for credential-based discovery
- Fix PROVIDER_DISCOVERY_FUNCTIONS incorrect azure/vertex mapping
- Extract business logic to api/credentials_service.py (service layer)
- Move credential Pydantic schemas to api/models.py
- Update tests to use new service imports and ValueError assertions
* fix: sanitize error responses and migrate key_provider to Credential
- Replace raw exception messages in all credential router 500 responses
with generic error strings (internal details logged server-side only)
- Refactor key_provider.py to use Credential.get_by_provider() instead
of deprecated ProviderConfig.get_instance()
- Remove unused functions (get_provider_configs, get_default_api_key,
get_provider_config) that were dead code
---------
Co-authored-by: JFMD <git@jfmd.us>
Co-authored-by: OraCatQAQ <570768706@qq.com>
* chore: bump surreal-commands to 1.3.1
Required for improved retry logging with command_id in error messages.
Ref #513
* chore: update uv.lock for surreal-commands 1.3.1
Update proxy configuration to use industry-standard environment variables
(HTTP_PROXY, HTTPS_PROXY, NO_PROXY) instead of custom variables.
The underlying libraries (esperanto, content-core, podcast-creator)
now automatically detect proxy settings from these standard variables.
- Bump content-core>=1.14.1 (fixes#494)
- Bump esperanto>=2.18
- Bump podcast-creator>=0.9
- Update documentation with new proxy configuration
* chore: bump content-core to support html to markdown
* feat: detect HTML content in clipboard for text sources
- Add paste handler to detect text/html format in clipboard
- Use HTML content instead of plain text when available
- Display info message when HTML is detected
- Add translations for all supported languages (en-US, pt-BR, ja-JP, zh-CN, zh-TW)
* fix: reset HTML detection banner on plain text paste
Clear the hasHtmlContent flag when pasting plain text (no HTML in
clipboard) so the banner doesn't persist incorrectly after replacing
HTML content with plain text.
* docs: update CHANGELOG for v1.6.0 release
* fix: connection error with llama.cpp and OpenAI-compatible providers
Bump Esperanto to 2.17.2 which fixes LangChain connection errors
caused by garbage collection closing shared HTTP clients.
Closes#465
* docs: update CHANGELOG for v1.6.0 release
* fix: improve error logging for chat model configuration issues (#358)
- Add detailed error logging in provision.py when model lookup fails
- Add warning logging in models.py when default model is not configured
- Add traceback logging in chat router exception handler
- Update Ollama docs with model name configuration guidance
- Update troubleshooting docs with "Failed to send message" solutions
- Bump version to 1.6.1
* chore: uvlock
* feat: content-type aware chunking and unified embedding
- Add chunking.py with HTML, Markdown, and plain text detection
- Add embedding.py with mean pooling for large content
- Create dedicated commands: embed_note, embed_insight, embed_source
- Use fire-and-forget pattern for embedding via submit_command()
- Refactor rebuild_embeddings_command to delegate to individual commands
- Remove legacy commands and needs_embedding() methods
- Reduce chunk size to 1500 chars for Ollama compatibility
- Update CLAUDE.md documentation for new architecture
Fixes#350, #142
* fix: address code review issues
- Note.save() now returns command_id for tracking embedding jobs
- Add length check after generate_embeddings() to fail fast on mismatch
- Add numpy as explicit dependency (was transitive)
- Remove hardcoded chunk sizes from docstrings
* docs: address code review comments
- Rename "SYNC PATH" to "DOMAIN MODEL PATH" in embedding router
- Add test_chunking.py and test_embedding.py to Testing Strategy
- Clarify auto-embedding behavior for each domain model
* fix: clean thinking tags from prompt graph output
Adds clean_thinking_content() to prompt.py to handle extended thinking
models that return <think>...</think> tags. This fixes empty titles
when saving notes from chat.
* chore: remove local docker-compose from git
* fix(frontend): handle null parent_id in search results
Add defensive check for null parent_id in search results to prevent
"Cannot read properties of null (reading 'split')" error. This can
happen with orphaned records in the database.
* fix: cascade delete embeddings and insights when source is deleted
When deleting a Source, now also deletes associated:
- source_embedding records
- source_insight records
This prevents orphaned records that cause null parent_id errors
in vector search results.
* fix: add cleanup for orphan embedding/insight records in migration 10
Deletes source_embedding and source_insight records where the
linked source no longer exists (source.id = NONE).
* chore: bump esperanto to 2.16
Increases ctx_num for Ollama models to accommodate larger notebook
context windows. See: https://github.com/lfnovo/esperanto/pull/69
* fix(i18n): resolve podcast dialog translation infinite loop and profile issues
- Remove incorrect translation keys for user-defined episode profiles
- Cache translation strings in ContentSelectionPanel to avoid repeated
Proxy accesses that triggered infinite loop detection
- Stabilize useEffect dependencies with dataKey pattern to prevent
re-initialization on every keystroke
- Replace unstable sourcesQueries prop with stable fetchingNotebookIds set
- Clean up unused getSourceModes function and TranslationKeys import
* chore: bump lock
* chore: bump version to 1.5.1 and update CHANGELOG
* fix: guard .join() call in dataKey when query data is undefined
* chore: post-i18n cleanup and version bump to 1.5.0
- Restore missing .dockerignore entries (notebook_data, surreal_data, docs, etc.)
- Fix lint command for Next.js 16 (use eslint directly instead of next lint)
- Remove aria-describedby={undefined} causing Radix UI warnings
- Bump version to 1.5.0
- Update CHANGELOG with i18n features
- Add multi-language UI mention to README
- Add i18n contribution guide to README.dev
- Document i18n system in CLAUDE.md files
Closes#344, #349, #360
* docs: fix provider order in CLAUDE.md to match layout.tsx
- Added custom exception handler to ensure CORS headers are included in
all HTTP error responses from the API
- Added documentation for 413 (Payload Too Large) errors when behind
reverse proxies (nginx, traefik, kubernetes ingress)
- Added client_max_body_size to nginx configuration examples
- Documented how to configure CORS headers for proxy-level error responses
Fixes#401
Addresses issue #362 - users were seeing hundreds of ERROR/WARNING logs
when processing large documents due to SurrealDB v2 transaction conflicts
during concurrent chunk embedding operations.
Changes:
- Upgraded to surreal-commands v1.3.0 which includes retry_log_level feature
- Increased retry attempts from 5 to 15 with max wait time 120s (from 30s)
to handle deep queues during concurrent processing
- Set retry_log_level to "debug" in embed_chunk and process_source commands
- Changed repository.py RuntimeError logging from ERROR to DEBUG level
- Updated command exception handlers to log retries at DEBUG level
- Updated documentation to reflect retry strategy
This is a temporary workaround for SurrealDB v2.x transaction conflict
issues with SEARCH indexes. Settings can be reduced after migrating to
SurrealDB v3 which fixes the underlying concurrency issue.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added remark-gfm plugin to enable GitHub Flavored Markdown support,
which includes proper table rendering. Updated all ReactMarkdown
usages with table styling components:
- ChatPanel (source and notebook chat)
- StreamingResponse (search/ask results)
- SourceInsightDialog
- SourceDetailContent
- TransformationPlayground
Tables now render with proper borders, headers with background,
and horizontal scroll for wide tables.
* feat(ui): add command palette for quick navigation and search
Replace top bar search with a command palette (⌘K / Ctrl+K) that provides:
- Quick navigation to all app sections
- Create shortcuts for sources, notebooks, and podcasts
- Theme switching (light/dark/system)
- Search and Ask functionality for non-matching queries
This approach saves screen real estate while providing faster access
to common actions through keyboard shortcuts.
Co-authored-by: EmbroiderSnow <1497411439@qq.com>
* chore: bump to 1.2.3
* feat(command-palette): add notebook quick navigation
Users can now type a notebook name in the command palette (⌘K) to
navigate directly to that notebook. Shows up to 8 most recent
notebooks, with cmdk filtering all notebooks when typing.
* fix(command-palette): address code review issues
- Skip ⌘K/Ctrl+K shortcut when focus is inside input, textarea, select,
or contentEditable elements to preserve native keyboard handling
- Remove 8-item limit on notebooks so all notebooks are searchable
via cmdk filtering
* perf(command-palette): memoize command matching and add platform shortcuts
- Memoize hasCommandMatch computation with useMemo to avoid
recalculating on every render
- Show platform-specific keyboard shortcut in sidebar hint:
⌘K on macOS, Ctrl+K on Windows/Linux
* fix(command-palette): add spinner to notebooks loading state
Show a spinning Loader2 icon alongside the "Loading notebooks..." text
for clearer visual feedback when the command palette is fetching data.
---------
Co-authored-by: EmbroiderSnow <1497411439@qq.com>
* fix: add missing overflow wrapper to notebooks list page
Adds flex-1 overflow-y-auto wrapper to enable proper scrolling
when notebook list exceeds viewport height. Matches the layout
pattern used by all other dashboard pages.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: reorder transformation routes to prevent dynamic route interception
Moved static routes (/transformations/execute and /transformations/default-prompt)
before dynamic routes (/transformations/{transformation_id}) to ensure FastAPI
matches them correctly. Previously, requests to static routes were incorrectly
captured by the dynamic route handler.
Fixes#250
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: bump to 1.2.1
* hide source and notes panel - fixes#193
* feat: improve layout for mobile views
* bump version to 1.2.2
* fix: address PR review feedback for collapsible columns
- Remove unused CollapseButton component from CollapsibleColumn.tsx
- Rename useCollapseButton to createCollapseButton (not a React hook)
- Move dialogs outside Card in SourcesColumn.tsx for consistency
- Add useMemo for collapseButton in both columns to prevent re-renders
* feat: support multiple sources
* fix: prevent ChatColumn double mounting on desktop
Add useIsDesktop hook to conditionally render mobile view only on
mobile screens. Previously, the mobile ChatColumn was hidden via CSS
on desktop but still mounted, causing duplicate hooks initialization
and redundant network requests.
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix: add missing overflow wrapper to notebooks list page
Adds flex-1 overflow-y-auto wrapper to enable proper scrolling
when notebook list exceeds viewport height. Matches the layout
pattern used by all other dashboard pages.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: reorder transformation routes to prevent dynamic route interception
Moved static routes (/transformations/execute and /transformations/default-prompt)
before dynamic routes (/transformations/{transformation_id}) to ensure FastAPI
matches them correctly. Previously, requests to static routes were incorrectly
captured by the dynamic route handler.
Fixes#250
Co-Authored-By: Claude <noreply@anthropic.com>
* chore: bump to 1.2.1
---------
Co-authored-by: Claude <noreply@anthropic.com>
* chore: improve podcast transcripts
* fix: remove date from insight - fixes#241
* fix: improve scrolling on source and insights - fixes#237
* chore: update esperanto to fix: #234
* chore: update esperanto to fix#226
* fix: process vectorization as subcommands to handle larger documents more gracefully - fix: #229
* feat: enable background job retry capabilities
* feat: reenable content types that were disabled during alpha version
* fix: remove unnecessary model caching causing many issues.
* feat: support multiple azure endpoints and keys just like openai compatible. Fixes#215
* docs: update azure variables
* chore: bump and update dependencies
* feat: prevent duplicate model names under same provider
Implement case-insensitive validation to prevent users from creating
duplicate model names under the same provider. This validation is
implemented both in the backend API and the frontend UI.
Changes:
- Backend: Add duplicate check in create_model endpoint (case-insensitive)
- Frontend: Add client-side validation in AddModelForm
- Frontend: Improve error message display from backend
- Tests: Add unit tests for duplicate model validation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* refactor: optimize duplicate model validation and improve error handling
- Replace O(n) model iteration with efficient SurrealDB query for duplicate check
- Improve error message to include model name and provider for better UX
- Remove frontend duplicate validation (backend-only enforcement)
- Fix test authentication by setting OPEN_NOTEBOOK_PASSWORD before imports
- Update test mocking to use repo_query instead of Model.get_all()
- Add pytest fixture for TestClient to ensure proper test isolation
All 11 tests passing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* remove unnecessary package
* fix: replace any with unknown type in error handler
- Change error type from 'any' to 'unknown' to satisfy ESLint
- Add proper type assertion for error object structure
- Maintains same runtime behavior with better type safety
---------
Co-authored-by: Claude <noreply@anthropic.com>