Skip to content

feat: support custom OPENAI_BASE_URL for self-hosted OpenAI-compatible LLM endpoints (#1996) - #2090

Open
Samarth1306w wants to merge 6 commits into
CapSoftware:mainfrom
Samarth1306w:feat/support-custom-openai-base-url-1996
Open

feat: support custom OPENAI_BASE_URL for self-hosted OpenAI-compatible LLM endpoints (#1996)#2090
Samarth1306w wants to merge 6 commits into
CapSoftware:mainfrom
Samarth1306w:feat/support-custom-openai-base-url-1996

Conversation

@Samarth1306w

@Samarth1306w Samarth1306w commented Aug 6, 2026

Copy link
Copy Markdown

Fixes #1996

Summary

Add optional environment variable support so self-hosted Cap instances can direct AI summaries, title generation, chapter extraction, and support chat to local or custom OpenAI-compatible endpoints (such as vLLM, LocalAI, or Ollama) instead of forcing cloud .

  1. ****: Added optional string parameter to server environment schema.
  2. ****: Replaced hardcoded in with configurable fallback.
  3. ****: Replaced hardcoded in messenger support agent with configurable fallback.

Greptile Summary

The PR adds configurable OpenAI-compatible endpoint support and also introduces CLI recording metadata lookup, a corresponding web metadata endpoint, rate limiting, and teleprompter adjustments.

  • Adds OPENAI_BASE_URL to the server environment and uses it for AI generation and messenger chat.
  • Adds cap recordings info and a GET video-metadata endpoint.
  • Adds rate limits for analytics tracking and guest checkout.
  • Adjusts desktop and mobile teleprompter layout behavior.

Confidence Score: 1/5

This PR should not merge until the password-protected metadata disclosure, malformed CLI share-URL handling, and strict TypeScript lint failure are fixed.

The new metadata route exposes AI metadata without the established password check, the CLI rejects valid share URLs with query parameters or trailing slashes, and the route introduces an explicit-any violation that breaks repository checks.

Files Needing Attention: apps/web/app/api/video/metadata/route.ts, apps/cli/src/recordings.rs, apps/web/tests/unit/rate-limit-ids.test.ts

Security Review

The new metadata endpoint bypasses the established password policy for public password-protected videos, allowing unauthenticated disclosure of titles, summaries, and chapters.

Important Files Changed

Filename Overview
apps/web/app/api/video/metadata/route.ts Adds metadata retrieval but bypasses password-protected video policy, violates the required HttpApi pattern, and introduces an explicit any.
apps/cli/src/recordings.rs Adds metadata fetching, but valid share URLs containing query parameters or a trailing slash are parsed incorrectly.
apps/web/workflows/generate-ai.ts Routes OpenAI chat-completion requests through the configured base URL with the existing default preserved.
apps/web/lib/messenger/agent.ts Applies the configurable OpenAI-compatible base URL to messenger completions while retaining the cloud fallback.
packages/env/server.ts Adds the optional OPENAI_BASE_URL server environment field.
apps/web/tests/unit/rate-limit-ids.test.ts Adds a source-reference contract test, with a repository comment-policy violation.
apps/mobile/src/recording/TeleprompterOverlay.tsx Preserves progress after repeated text layouts; no concrete current playback regression was established.
apps/desktop/src/routes/teleprompter.tsx Preserves the existing scroll position while resizing before playback.
Prompt To Fix All With AI
### Issue 1
apps/web/app/api/video/metadata/route.ts:60-62
**Password policy bypassed for metadata**

When a public video is password-protected, this check authorizes unauthenticated and non-owner requests solely because `video.public` is true, exposing its title, AI summary, chapters, and generation status without the required password.

**How this was verified:** The video schema permits public videos with passwords, while the established public-view policy verifies password candidates before granting access.

### Issue 2
apps/cli/src/recordings.rs:110-118
**Share URL suffix becomes video ID**

When a valid share URL contains a query parameter or trailing slash, `rsplit('/')` includes the query suffix or returns an empty segment, causing `cap recordings info` to send the wrong `videoId` and receive a 400 or 404 instead of metadata.

### Issue 3
apps/web/app/api/video/metadata/route.ts:64
**Explicit any breaks lint contract**

The new `Record<string, any>` cast violates the repository's enabled `noExplicitAny` rule, causing the changed route to fail the required Biome check; use a defined metadata type or `unknown` with narrowing.

### Issue 4
apps/web/app/api/video/metadata/route.ts:43
**Metadata route bypasses API architecture**

This new operation uses an ad-hoc Next.js handler and direct database query instead of the required `HttpApi` builder and backend service pattern, bypassing shared policy, typed errors, and request-context wiring.

### Issue 5
apps/web/__tests__/unit/rate-limit-ids.test.ts:6-7
**Comments duplicate constant purpose**

These comments only narrate the self-descriptive `UNWIRED_RATE_LIMIT_IDS` constant rather than preserving a non-obvious invariant or workaround, adding redundant text that must be maintained.

```suggestion

```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "feat: support custom OPENAI_BASE_URL for..." | Re-trigger Greptile

Greptile also left 5 inline comments on this PR.

Context used (6)

@superagent-security

Copy link
Copy Markdown

🚨 Contributor flagged. Click here for more info: Superagent Dashboard

Comment on lines +60 to +62
if (!video.public && video.ownerId !== user?.id) {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 security Password policy bypassed for metadata

When a public video is password-protected, this check authorizes unauthenticated and non-owner requests solely because video.public is true, exposing its title, AI summary, chapters, and generation status without the required password.

How this was verified: The video schema permits public videos with passwords, while the established public-view policy verifies password candidates before granting access.

Knowledge Base Used: Web App (apps/web)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/app/api/video/metadata/route.ts
Line: 60-62

Comment:
**Password policy bypassed for metadata**

When a public video is password-protected, this check authorizes unauthenticated and non-owner requests solely because `video.public` is true, exposing its title, AI summary, chapters, and generation status without the required password.

**How this was verified:** The video schema permits public videos with passwords, while the established public-view policy verifies password candidates before granting access.

**Knowledge Base Used:** [Web App (apps/web)](https://app.greptile.com/cap/-/custom-context/knowledge-base/capsoftware/cap/-/docs/web-app.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +110 to +118
pub async fn info(url_or_id: String, format: OutputFormat) -> Result<(), String> {
let video_id = if url_or_id.contains('/') {
url_or_id
.rsplit('/')
.next()
.unwrap_or(&url_or_id)
.to_string()
} else {
url_or_id

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Share URL suffix becomes video ID

When a valid share URL contains a query parameter or trailing slash, rsplit('/') includes the query suffix or returns an empty segment, causing cap recordings info to send the wrong videoId and receive a 400 or 404 instead of metadata.

Knowledge Base Used: Cap CLI (apps/cli)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/cli/src/recordings.rs
Line: 110-118

Comment:
**Share URL suffix becomes video ID**

When a valid share URL contains a query parameter or trailing slash, `rsplit('/')` includes the query suffix or returns an empty segment, causing `cap recordings info` to send the wrong `videoId` and receive a 400 or 404 instead of metadata.

**Knowledge Base Used:** [Cap CLI (`apps/cli`)](https://app.greptile.com/cap/-/custom-context/knowledge-base/capsoftware/cap/-/docs/cli.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

return Response.json({ error: "Unauthorized" }, { status: 401 });
}

const meta = (video.metadata as Record<string, any>) ?? {};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Explicit any breaks lint contract

The new Record<string, any> cast violates the repository's enabled noExplicitAny rule, causing the changed route to fail the required Biome check; use a defined metadata type or unknown with narrowing.

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/app/api/video/metadata/route.ts
Line: 64

Comment:
**Explicit any breaks lint contract**

The new `Record<string, any>` cast violates the repository's enabled `noExplicitAny` rule, causing the changed route to fail the required Biome check; use a defined metadata type or `unknown` with narrowing.

**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

return Response.json(true, { status: 200 });
}

export async function GET(request: NextRequest) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Metadata route bypasses API architecture

This new operation uses an ad-hoc Next.js handler and direct database query instead of the required HttpApi builder and backend service pattern, bypassing shared policy, typed errors, and request-context wiring.

Context Used: AGENTS.md (source)

Knowledge Base Used: Web App (apps/web)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/app/api/video/metadata/route.ts
Line: 43

Comment:
**Metadata route bypasses API architecture**

This new operation uses an ad-hoc Next.js handler and direct database query instead of the required `HttpApi` builder and backend service pattern, bypassing shared policy, typed errors, and request-context wiring.

**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))

**Knowledge Base Used:** [Web App (apps/web)](https://app.greptile.com/cap/-/custom-context/knowledge-base/capsoftware/cap/-/docs/web-app.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment on lines +6 to +7
// Rate limit IDs declared in advance for firewall rules or separate app packages
// that are intentionally not yet wired in apps/web endpoints.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Comments duplicate constant purpose

These comments only narrate the self-descriptive UNWIRED_RATE_LIMIT_IDS constant rather than preserving a non-obvious invariant or workaround, adding redundant text that must be maintained.

Suggested change
// Rate limit IDs declared in advance for firewall rules or separate app packages
// that are intentionally not yet wired in apps/web endpoints.

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/__tests__/unit/rate-limit-ids.test.ts
Line: 6-7

Comment:
**Comments duplicate constant purpose**

These comments only narrate the self-descriptive `UNWIRED_RATE_LIMIT_IDS` constant rather than preserving a non-obvious invariant or workaround, adding redundant text that must be maintained.

```suggestion

```

**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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.

Support custom base URLs for self-hosted AI providers (vLLM, self-hosted Whisper, etc.)

2 participants