Skip to content

♻️ refactor: Native Prompts Behind a Shared Service and Adapter - #16506

Open
AtefBellaaj wants to merge 2 commits into
devfrom
atef/ai-2154-native-prompt-adapter-spike
Open

AtefBellaaj wants to merge 2 commits into
devfrom
atef/ai-2154-native-prompt-adapter-spike

Conversation

@AtefBellaaj

@AtefBellaaj AtefBellaaj commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

The prompt route handlers in api/server/routes/prompts.js hold the selection, validation, content-protection and mutation rules for native prompts and call the database methods directly, so a second prompt source would have to copy all of them.

This PR moves that behavior into packages/api/src/prompts. A shared prompt service owns validation, content protection, catalog projection, Usage and the creator ownership grant. A native source adapter owns Production and exact-revision selection over the data-schemas methods, with plain string IDs. HTTP handlers map service results and stage-tagged database errors to each route's existing status and body. /api keeps only the router, middleware and wiring. The access middleware passes the record it loaded to the handler, so the handler does not read it again.

The data-schemas prompt methods now throw on database failure instead of returning { message }, and take name and category for listing instead of a Mongo filter. The service takes one source adapter, and every prompt group uses it.

How it works

api/server/routes/prompts.js          # auth, role gates, access middleware, registration
  canAccessPromptGroupResource / canAccessPromptViaGroup
    createPromptAccessResolvers       # loads the record once → req.resourceAccess.resourceInfo
  createPromptHandlers                # legacy status/body mapping per route
    createPromptServiceFromDb         # wires the service to the data-schemas methods
      createPromptService             # validation, protection, projection, ownership grant
        createNativePromptAdapter     # selection, reads and mutations, string IDs
        createPromptCatalogStore      # listing, update, usage, deletion
          data-schemas prompt methods # throw on failure, null only for absence

Database failures are wrapped in PromptStoreError with a read or write stage. The handler uses the stage to keep the existing response, for example 200 { message: 'Error saving prompt' } when a revision save fails. A source that cannot perform a mutation leaves it out, and the service returns an unsupported result.

The access resolvers return null for a malformed ID or a missing record, which gives the access check's 404. A read failure propagates, which gives the access check's 500.

Type of change

  • Refactor
  • Tests / tooling / CI

Testing

  • Service, native adapter and access resolver specs run against mongodb-memory-server with the real data-schemas methods. They cover Production and exact resolution, reuse of loaded records, a stored revision without a type, absence versus a filtered record, malformed IDs, stage-tagged and propagated read failures, payload validation for new groups and revisions, the ownership grant and its failure, and a source without mutations.
  • Route compatibility tests cover the legacy failure bodies, 404 for a malformed ID and 500 for a failed read in the access check, 400 for a new group without a prompt type, promotion without mutation after a failed read, a missing groupId, and one read per loaded-record route.
  • npx tsc --noEmit in packages/api and packages/data-schemas.
  • Jest at 518137864: packages/api src/prompts 100 passed; data-schemas src/methods/prompt 46 passed; api prompt routes, controllers and access middleware 1625 passed, 1 skipped.
  • npm run static-checks -- --against origin/dev passed.

Risk / compatibility

Each route keeps its status codes and bodies, except:

  • GET /api/prompts without groupId returns 400 { error: 'Invalid or missing groupId' }. It no longer returns the caller's own prompts, or all prompts for READ_PROMPTS callers.
  • POST /api/prompts validates the first revision like a new revision: a missing or unknown type returns 400 { error: 'Prompt type must be "text" or "chat"' }. Before, the group was created and the revision was stored without a type. The client form always sends a type.
  • A database failure inside the prompt access check returns the access check's 500. Before, the group check could not tell a failure from a record, and the revision check returned 404.
  • When the first read of a capability-bypass promotion fails, the route returns 500 and Production does not change.
  • Prompt group name search matches the text as entered, not as a regular expression.

Stored revisions without a type stay readable; selection treats them as text, the same default the client uses. No schema, stored data or configuration changes.

Checklist

  • I reviewed my own changes
  • Relevant tests have been added or updated
  • Existing relevant tests pass
  • The change does not introduce new warnings or errors
  • User-facing or complex behavior is documented where necessary
  • Required dependency changes have been merged/published
  • Required documentation PR: N/A

🤖 Generated with Claude Code

@AtefBellaaj

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: baedb6a7f0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/api/src/prompts/access.ts Outdated
Comment on lines +28 to +29
logger.error('[resolvePromptGroup] Error reading prompt group', error);
return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Propagate database failures from access resolution

When the prompt-group query throws for an ordinary request, this catch converts the operational failure to null; canAccessResource then interprets it as a missing resource and returns 404. The analogous prompt resolver below does the same, so a transient database outage makes existing prompts appear deleted instead of producing the middleware's 500 response. Let query failures propagate and reserve null for an actual absence.

AGENTS.md reference: AGENTS.md:L108-L120

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

fixed

Comment thread packages/api/src/prompts/native.ts Outdated
revision != null &&
revision._id != null &&
revision.groupId === groupId &&
revision.type != null &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Resolve prompt revisions that lack a type

When a group was created with the still-supported initial payload { prompt: '...' } without type, or an existing stored revision predates that field, this predicate rejects both the populated revision and the subsequent database read. Consequently resolvePrompt reports the production or exact selection as unavailable even though the revision exists. Normalize such revisions to a compatible default or migrate/validate them without breaking existing stored prompts.

AGENTS.md reference: AGENTS.md:L49-L52

Useful? React with 👍 / 👎.

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.

Fixed in bc53813fe39859fcf6e13124f9adcb672bcb48d8 and verified on f51d81f29094614f4c704b88b8355127598d4b89. Missing revision types resolve as text; the real-database native adapter tests cover Production and exact selection.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-30T09:02:50.932122Z bc53813 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@AtefBellaaj
AtefBellaaj force-pushed the atef/ai-2154-native-prompt-adapter-spike branch 2 times, most recently from 91a0207 to 5181378 Compare September 29, 2026 20:22
@AtefBellaaj

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5181378642

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread api/server/services/Prompts/index.js Outdated
let promptService;

function getPromptService() {
promptService ??= createPromptServiceFromDb({ db, grantPermission });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep the prompt service lifecycle out of legacy CJS

The ??= branch makes legacy /api own a process-wide lazy service lifecycle, so alternate prompt sources, isolated app instances, or dependency resets must now work around this module cache instead of receiving an injected service. Keep this CJS layer to requires and calls into the TypeScript module, and have the application composition pass the constructed service to its consumers.

AGENTS.md reference: AGENTS.md:L77-L81

Useful? React with 👍 / 👎.

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.

Fixed in bc53813fe39859fcf6e13124f9adcb672bcb48d8 and verified on f51d81f29094614f4c704b88b8355127598d4b89. The lazy CJS service cache is gone. The router constructs the TypeScript service with its database and permission dependencies and passes it into the handlers. Prompt service and route suites pass.

@AtefBellaaj
AtefBellaaj force-pushed the atef/ai-2154-native-prompt-adapter-spike branch from 5181378 to a9098c2 Compare September 30, 2026 08:53
@AtefBellaaj
AtefBellaaj force-pushed the atef/ai-2154-native-prompt-adapter-spike branch from a9098c2 to bc53813 Compare September 30, 2026 08:54
@AtefBellaaj

Copy link
Copy Markdown
Collaborator Author

@codex review again

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: bc53813fe3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@AtefBellaaj
AtefBellaaj marked this pull request as ready for review September 30, 2026 11:46
@codegraph-librechat codegraph-librechat Bot added the 🗺️ Prompt Library codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) label Oct 1, 2026
@lia-by-librechat

Copy link
Copy Markdown
Contributor

Head f51d81f29094614f4c704b88b8355127598d4b89 merges current dev and resolves the data-schemas export conflict while preserving both prompt and agent exports. Final review and focused verification are running against this head.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🗺️ Prompt Library codegraph: the taxonomy area this belongs to (classifier, confidence ≥ 0.9) 🛡️ security review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants