Skip to main content
LibreChat is joining ClickHouse to power the open-source Agentic Data Stack 🎉 Learn more
LibreChat

Conversation Pull Requests

Show the GitHub pull request, size and CI status for chats that run code in an attached workspace.

When a chat runs code in an attached workspace, the worker reports the git branch and commit it is on. LibreChat uses that branch to find the matching GitHub pull request, with a token held on the server, and shows it in two places:

  • Chat header: a chip with the pull request icon and a CI dot. Hovering or focusing it opens a card.
  • Sidebar conversation list: the same icon and CI dot at the end of the row. The row keeps the conversation's own title; the pull request title appears only in the card.

Requirements

All of these must be true for a chat to show a pull request:

  1. LibreChat has a GitHub token and a repository scope. See Setup.
  2. The chat uses an attached code workspace (a code worker), not a plain chat.
  3. The worker reports its branch. This needs a worker built from LibreChat-AI/code-interpreter PR #311 or later, and CODEAPI_BRIDGE_LANE_GIT=true set on the Code API. See Code Interpreter.

If a requirement is missing, chats simply show no pull request and no error is shown to the user. The exception is a token that is set but whose environment variable is empty or missing: lookups then fail with NOT_CONFIGURED, and sidebar rows show the warning icon with a retry button.

Setup

1. Create a token

Use a fine-grained GitHub token limited to the repositories you want to show, with read-only permissions:

  • Pull requests
  • Checks
  • Contents

Permission failure modes

Without Checks, every matched pull request fails to load. Without Contents, a chat whose branch has moved on since its last command fails to load.

2. Give LibreChat the token

Pending: not yet released

Environment variable fallback is part of the default-on change (LibreChat-AI/LibreChat#16876).

LibreChat looks for a token in this order:

  1. token: "${MY_VAR}" in librechat.yaml. This is an environment variable reference; the YAML never holds the token itself.
  2. If token is not set, the first of these environment variables that is set: GITHUB_PULL_REQUEST_TOKEN, GITHUB_TOKEN, GH_TOKEN.
  3. No token anywhere: the feature stays dormant.

A token that is set but does not resolve (the variable is empty or missing) fails with NOT_CONFIGURED. It does not fall back to the three variables above.

GITHUB_TOKEN is a common name

If your deployment already uses GITHUB_TOKEN for something else, the default-on behavior starts using it here as soon as a repository scope is set. Set token explicitly, or use GITHUB_PULL_REQUEST_TOKEN, to avoid sharing it.

Older versions: token is required when the feature is enabled, and must be a ${NAME} reference such as "${GITHUB_PULL_REQUEST_TOKEN}". There is no environment variable fallback.

3. Choose which repositories it may look up

The worker reports its own repository, so the server must decide which repositories the token may be used for. Without a scope the feature stays dormant. There are two ways:

  • allowedRepositories: a list of owner/name or owner/* entries (up to 256). Matching is case-insensitive. Entries such as */*, *, owner, or ones containing .. are rejected by the schema.
  • allowAllRepositories: true (Pending): look up any repository the token can read. Default is false.

Security: allowAllRepositories

With this on, any user who can run code can point the server's token at any repository that token can read, and see the pull request title, size and check status. Use a read-only token scoped to the repositories you are willing to show.

4. Turn it on

Pending: not yet released

Default-on behavior is part of LibreChat-AI/LibreChat#16876.

The feature is on as soon as there is a token and a repository scope. There is no enabled switch to set. enabled: false turns it off, whatever else is set.

Older versions: enabled: true is required, and the schema rejects enabled: true without both token and a non-empty allowedRepositories.

Minimal examples

Default-on form (Pending), with GITHUB_TOKEN set in the server environment:

endpoints:
  agents:
    pullRequests:
      allowAllRepositories: true

Restricted to specific repositories (Pending, same token fallback):

endpoints:
  agents:
    pullRequests:
      allowedRepositories:
        - 'LibreChat-AI/LibreChat'
        - 'my-org/*'

Form that works on older versions:

endpoints:
  agents:
    pullRequests:
      enabled: true
      token: '${GITHUB_PULL_REQUEST_TOKEN}'
      allowedRepositories: ['LibreChat-AI/LibreChat']

See the pullRequests reference for every key, including the tuning options.

What users see

Chat header chip

  • Desktop: the chip sits on the left of the header, next to the other left controls. Hovering or focusing it opens the card to its right.
  • Mobile: the entry is in the header's overflow (three dots) menu and opens a dialog.
  • The header refreshes on its own: about every 20 seconds while checks are running or mergeability is still being computed, about every 60 seconds otherwise, and not at all once a pull request is merged or closed with no checks running.
  • A pull request icon with a CI dot sits at the end of the conversation row. It appears only for conversations that have a pull request.
  • Hover, focus or click opens the same card to the right of the mark. Clicking the mark does not open the conversation.
  • The sidebar never polls. A row is as fresh as the last time the list loaded, and it refreshes when the window regains focus and its data is older than a minute.
  • If a lookup fails, the row shows a warning icon with a retry button in its card, so a failure is not mistaken for "no pull request".

The card

The card shows PR #number, +additions in green, -deletions in red, a link that opens the pull request on GitHub, the pull request title, and two badges:

  • State: Open, Draft, Merged or Closed.
  • Checks: Passing, Failing, Running, or none.

Icon and dot meaning

SituationIcon colorCI dot
Open, no conflicts, checks passingGreen (ready to merge)Green
Open, no conflicts, no checks at allGreenNone
Open, merge conflictsRed (cannot merge)By checks
DraftNeutralBy checks
Merged or closedNeutralNone (amber while checks still run)

CI dot colors: green for passing, red for failing, amber for running, and none when there are no checks, or when the pull request is merged or closed and no checks are still running. A failed check outranks one still running. A rollup with more check runs than were read is never reported as passing.

How a pull request is matched

  1. The worker reports the repository, branch and commit (head) the chat last ran at.
  2. LibreChat lists open pull requests for that branch first, so closed history on a reused branch name cannot hide an open one. Then it checks open pull requests from forks (found through the commit), then closed ones.
  3. With a recorded commit, a pull request counts only if its head is that commit or builds on it. This keeps a deleted and reused branch name from showing an old pull request.
  4. A branch with no commit yet (a new, empty branch) matches nothing.
  5. The result is cached per credential, repository, branch and commit.
  6. Check runs are read for the pull request's current head. For a pull request from a fork, checks are read from the fork only when that fork's repository is in scope (listed, or allowAllRepositories). Otherwise the base repository is read and the token never goes to an unlisted fork.

Behavior

Rate limits

  • A GitHub rate limit applies to the credential, so it pauses lookups for every branch under that token.
  • The minimum pause is 10 seconds. A secondary limit with no wait given waits 60 seconds. A wait that GitHub names is honored, capped at 1 hour.
  • During a pause, lookups answer RATE_LIMITED and rows show the retry state.

Rolling upgrades

  • The sidebar asks the batch route only when the startup config says the server has it (pullRequestsBatchVersion).
  • If a replica without the route answers 404, the client falls back to the single route, one call at a time, or at pullRequestsMaxConcurrentLookups when the server advertised it. This covers a client that received the new startup flag from an upgraded replica and then reached a replica without the route. If /api/config is served by a replica without the flag, the sidebar never asks the batch route and rows stay empty, so upgrade every replica before relying on sidebar marks.

Defaults when unset

  • With no token or no repository scope, the feature is dormant: nothing is advertised in the startup config, nothing is recorded, nothing is shown.
  • With the feature off in the config, existing chats are unchanged and the client never calls the new routes.

Security model

  • The token lives on the server. It is never sent to the browser, and error responses never include GitHub's response text, the token, or stack traces.
  • The token is used only for repositories in scope. It is never sent to a fork that is not in scope.
  • The two /api/convos lookup routes require an authenticated user and are owner-scoped. A conversation that is missing, expired, or belongs to another user looks the same as one with no pull request.
  • Use a read-only, fine-grained token limited to the repositories you are willing to show.

Troubleshooting

SymptomLikely causeFix
No chip and no sidebar mark anywhereNo token, no repository scope, or enabled: falseCheck Setup. Open /api/config while signed in and look for pullRequestsEnabled: true.
Header chip works, sidebar rows show nothingpullRequestsBatchVersion is missing from /api/config (old build, or a stale tab)Rebuild or upgrade the server, restart, and hard reload. Confirm pullRequestsBatchVersion: 1.
Chip appears for some chats onlyOnly chats on an attached code workspace that has reported a branch have a laneConfirm CODEAPI_BRIDGE_LANE_GIT=true on the Code API and a worker built from code-interpreter PR #311 or later.
Warning icon with retryLookup failed (rate limit, GitHub error, or NOT_CONFIGURED)Retry. If it is constant, check the token and its permissions.
NOT_CONFIGUREDtoken is set but its variable is empty or missingSet the variable. Pending: or remove token to use the fallback variables.
Pull requests load but checks look wrong or failThe token lacks Checks or Contents permissionGrant read-only Checks and Contents.
Pull request from a fork shows no checksThe fork is not in scope, so checks are read from the base repositoryAdd the fork owner to allowedRepositories. Pending: or set allowAllRepositories: true.
No pull request for a branch name that has been reused many timesmaxCandidatePullRequests, maxCandidatePages or maxHeadComparisons is too low for how often the name is reused, so the search stops before it reaches the matching pull requestRaise them.
Slow or timing-out lookups behind a proxyrequestTimeoutSeconds is too lowRaise it. Check HTTPS_PROXY and NO_PROXY.

API reference

Both /api/convos routes below require an authenticated user and are owner-scoped. The startup config endpoint is not owner-scoped.

Get one conversation's pull request

GET /api/convos/:conversationId/pull-request

  • 200: { "pullRequest": null } or { "pullRequest": { number, title, url, additions, deletions, state, isDraft, mergeable, checks } }.
  • state is open, closed or merged. mergeable is clean, conflicting or unknown. checks is passing, failing, running or none.
  • 503: { "error": "...", "code": "NOT_CONFIGURED" | "RATE_LIMITED" | "UPSTREAM_ERROR" }.
  • 404: invalid conversation id. 500: storage error with fixed text.

Get many conversations' pull requests

POST /api/convos/pull-requests

  • Body: { "conversationIds": ["..."] }, with 1 to 50 ids. Duplicates collapse. Anything else is a 400.
  • 200: { "results": [ { "conversationId", "pullRequest" } | { "conversationId", "error": { "code" } } ] }, in the order asked. One failing lookup does not hide the others.
  • A missing token fails only the entries that needed it.

Startup config

GET /api/config includes these fields. The server sets them; they are not settings.

  • pullRequestsEnabled (boolean)
  • pullRequestsBatchVersion (1, present only with the feature on)
  • pullRequestsMaxConcurrentLookups (number, present only with the feature on)

Only the error codes listed above are stable.

How is this guide?