-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Feat/server prompt templates #1086
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
5211225
WIP: add server prompt template feature
sedanah-m ce66714
clean up
sedanah-m a96a189
more clean up
sedanah-m 2fb02e6
cleanup
sedanah-m c006f02
renaming
sedanah-m 0a3b122
fix typo
sedanah-m 44a7e6b
fix typo
sedanah-m a414a65
refactor some descriptions and clean up typo
sedanah-m 7f988f7
add get `AiTemplateModel`
sedanah-m 7d78013
format doc
sedanah-m ecf9781
add a comment; remove unnecessary steps in readme.
sedanah-m File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
ai/ai-samples/src/features/server-prompt-templates/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| # Firebase AI Logic: Server Prompt Templates Sample | ||
|
|
||
| This quickstart demonstrates how to fetch and execute centrally managed prompt templates stored in the Firebase Console using the Firebase AI Logic SDK. | ||
|
|
||
| ## Why Server Prompt Templates? | ||
|
|
||
| - **No deployment updates**: Change prompts, tune system instructions, or switch underlying models directly in the Firebase Console without having to release a new app version. | ||
| - **Security**: Protect against exposing your prompt client-side. | ||
|
|
||
| --- | ||
|
|
||
| ## 1. Firebase Console Setup | ||
|
|
||
| Before running this sample, create and lock the template in your Firebase project: | ||
|
|
||
| 1. Open the [Firebase Console](https://console.firebase.google.com/) and select your project. | ||
| 2. In the left navigation, navigate to **AI Logic > Prompt Templates**. | ||
| 3. Click **Create Template**. Note that these starter templates provide the format and syntax for some common use cases and this tutorial assumes you've selected the `Input + System Instructions` option. Once you clicked that, configure the following: | ||
| - **Template ID**: `invoice-generator` | ||
| - **Model**: `gemini-3.5-flash-lite` (or any available Gemini model) | ||
| 4. In the **Prompt Content** / **System Instructions** text area, paste the following prompt: | ||
| ```text | ||
| {{role "system"}} | ||
| All output must be a clearly structured invoice document. | ||
| Use a tabular or clearly delineated list format for line items. | ||
|
|
||
| {{role "user"}} | ||
| Create an example customer invoice for a customer named {{customerName}}. | ||
| ``` | ||
| 5. Click **Save**. | ||
| 6. **Important**: While client applications can execute both unlocked (draft) and locked templates during development, you should always **Lock** your template before deploying to production. Locking freezes the prompt configuration, ensuring that subsequent console edits do not accidentally change your production app's behavior. | ||
|
|
||
| --- | ||
|
|
||
| ## 2. Running the Sample Locally | ||
|
|
||
| Run the sample inside the full app shell: | ||
| ```bash | ||
| npm run dev | ||
| ``` | ||
|
|
||
| Or run this feature directly in isolated mode: | ||
| ```bash | ||
| npm run dev:template | ||
| ``` | ||
|
|
||
| Open your browser to the local URL (e.g., `http://localhost:***` provided in the console). | ||
|
|
||
| --- | ||
|
|
||
| ## 3. Possible Troubleshooting & Failure Surfaces | ||
|
|
||
| | Issue | Cause | Resolution | | ||
| |---|---|---| | ||
| | `NOT_FOUND` / 404 | Template ID mismatch or template doesn't exist in the active Firebase project | Verify that the template ID in your code is exactly `invoice-generator` and that you are initialized in the correct Firebase Project | | ||
| | Missing Variable output | Variable names in client code don't match console | Ensure the keys passed to `templateVariables` match the `{{variable}}` placeholders in your prompt template. | | ||
| | `PERMISSION_DENIED` | Firebase AI Logic API not enabled or App Check blocked | Follow the Firebase AI Logic guided setup in the console and ensure your API key / App Check tokens are valid. | |
148 changes: 148 additions & 0 deletions
148
ai/ai-samples/src/features/server-prompt-templates/index.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| import { useState } from 'react'; | ||
| import { generateFromTemplate } from './service'; | ||
|
|
||
| export default function ServerPromptTemplatesView() { | ||
| const [templateId, setTemplateId] = useState('invoice-generator'); | ||
| const [customerName, setCustomerName] = useState('Jane Doe'); | ||
|
|
||
| const [response, setResponse] = useState(''); | ||
| const [loading, setLoading] = useState(false); | ||
| const [error, setError] = useState<string | null>(null); | ||
|
|
||
| const handleExecute = async () => { | ||
| const trimmedId = templateId.trim(); | ||
| if (!trimmedId) { | ||
| setError('Please enter a Template ID.'); | ||
| return; | ||
| } | ||
|
|
||
| const trimmedCustomerName = customerName.trim(); | ||
| if (!trimmedCustomerName) { | ||
| setError('Please enter a Customer Name.'); | ||
| return; | ||
| } | ||
|
|
||
| setLoading(true); | ||
| setError(null); | ||
| setResponse(''); | ||
|
|
||
| const templateVariables = { | ||
| customerName: trimmedCustomerName, | ||
| }; | ||
|
|
||
| try { | ||
| const text = await generateFromTemplate(trimmedId, templateVariables); | ||
| setResponse(text); | ||
| } catch (err: unknown) { | ||
| const message = | ||
| err instanceof Error ? err.message : 'An error occurred while executing the template.'; | ||
| setError(message); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| }; | ||
|
|
||
| const isNotFoundError = | ||
| error && (error.toLowerCase().includes('not found') || error.toLowerCase().includes('not_found')); | ||
|
|
||
| return ( | ||
| <div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}> | ||
| <h2>Server Prompt Templates</h2> | ||
| <p style={{ color: '#666', marginBottom: '15px' }}> | ||
| Centrally manage, test, and update AI prompts in the Firebase Console without redeploying client code. | ||
| </p> | ||
|
|
||
| {/* Prerequisite Setup Notice */} | ||
| <div | ||
| style={{ | ||
| backgroundColor: '#e8f0fe', | ||
| border: '1px solid #c2e7ff', | ||
| borderRadius: '6px', | ||
| padding: '12px 16px', | ||
| marginBottom: '20px', | ||
| fontSize: '14px', | ||
| color: '#174ea6', | ||
| }} | ||
| > | ||
| <strong>Console Prerequisite:</strong> Requires a published/locked template in the Firebase Console. | ||
| <strong>Console Prerequisite:</strong> Requires a saved template in the Firebase Console. | ||
| See <code>README.md</code> in this feature folder for the setup guide. | ||
| </div> | ||
|
|
||
| <div style={{ marginBottom: '15px' }}> | ||
| <label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}> | ||
| Template ID: | ||
| </label> | ||
| <input | ||
| type="text" | ||
| value={templateId} | ||
| onChange={(e) => setTemplateId(e.target.value)} | ||
| placeholder="invoice-generator" | ||
| style={{ width: '100%', padding: '8px', boxSizing: 'border-box' }} | ||
| /> | ||
| </div> | ||
|
|
||
| <div style={{ marginBottom: '15px' }}> | ||
| <label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold' }}> | ||
| Customer Name (<code>{'{{customerName}}'}</code>): | ||
| </label> | ||
| <input | ||
| type="text" | ||
| value={customerName} | ||
| onChange={(e) => setCustomerName(e.target.value)} | ||
| style={{ width: '100%', padding: '8px', boxSizing: 'border-box' }} | ||
| /> | ||
| </div> | ||
|
|
||
| <button | ||
| onClick={handleExecute} | ||
| disabled={loading} | ||
| style={{ | ||
| padding: '10px 20px', | ||
| cursor: loading ? 'not-allowed' : 'pointer', | ||
| backgroundColor: loading ? '#ccc' : '#007BFF', | ||
| color: '#fff', | ||
| border: 'none', | ||
| borderRadius: '4px', | ||
| marginBottom: '15px', | ||
| }} | ||
| > | ||
| {loading ? 'Executing Template...' : 'Execute Template'} | ||
| </button> | ||
|
|
||
| {error && ( | ||
| <div | ||
| style={{ | ||
| color: '#D8000C', | ||
| backgroundColor: '#FFD2D2', | ||
| padding: '12px', | ||
| marginTop: '15px', | ||
| borderRadius: '4px', | ||
| fontSize: '14px', | ||
| lineHeight: '1.4', | ||
| }} | ||
| > | ||
| <strong>Error:</strong> {error} | ||
| {isNotFoundError && ( | ||
| <div style={{ marginTop: '10px', paddingTop: '8px', borderTop: '1px solid #ffbaba' }}> | ||
| <strong>Setup Checklist:</strong> | ||
| <ul style={{ margin: '6px 0 0 16px', padding: 0 }}> | ||
| <li>Template <code>{templateId}</code> exists in your Firebase project?</li> | ||
| <li>Template status is <strong>Locked / Published</strong> (drafts cannot be called by client SDKs)?</li> | ||
| <li>Template is saved in the Firebase Console (and locked for production)?</li> | ||
| <li>Your web app configuration matches the Firebase project where the template is stored?</li> | ||
| </ul> | ||
| </div> | ||
| )} | ||
| </div> | ||
| )} | ||
|
|
||
| {response && ( | ||
| <div style={{ marginTop: '20px', borderTop: '1px solid #eee', paddingTop: '15px' }}> | ||
| <h3>Response:</h3> | ||
| <p style={{ whiteSpace: 'pre-wrap', lineHeight: '1.5' }}>{response}</p> | ||
| </div> | ||
| )} | ||
| </div> | ||
| ); | ||
| } |
28 changes: 28 additions & 0 deletions
28
ai/ai-samples/src/features/server-prompt-templates/service.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import { getAiTemplateModel } from '../../services/firebaseAIService'; | ||
|
|
||
| export interface InvoiceTemplateVariables { | ||
| customerName: string; | ||
| } | ||
|
|
||
| /** | ||
| * Executes a server-side prompt template in a single unary request. | ||
| * Centrally managed templates allow updating prompts and models without redeploying client code. | ||
| * | ||
| * @param templateId The ID of the locked template in the Firebase Console (e.g. 'invoice-generator') | ||
| * @param variables Key-value map matching the {{variables}} declared in the template | ||
| * @returns The text string generated by the model | ||
| */ | ||
| export async function generateFromTemplate( | ||
| templateId: string, | ||
| variables: Record<string, unknown> | ||
| ): Promise<string> { | ||
| try { | ||
| const model = getAiTemplateModel(); | ||
| const result = await model.generateContent(templateId, variables); | ||
| return result.response.text(); | ||
| } catch (error: unknown) { | ||
| throw error instanceof Error | ||
| ? error | ||
| : new Error('An unknown error occurred during template generation.'); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.