Skip to content

Commit 4a771fc

Browse files
pr-Maisdackers86google-cloud-firebase-extensions-botcabljacmhadaily
authored
release: dec 2023 (#289)
Co-authored-by: Mais <[email protected]> Co-authored-by: Darren Ackers <[email protected]> Co-authored-by: google-cloud-firebase-extensions-bot <[email protected]> Co-authored-by: Jacob Cable <[email protected]> Co-authored-by: Majid <[email protected]>
1 parent 89a9a1a commit 4a771fc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+33389
-94
lines changed

.github/workflows/create-release-candidates.yaml

Lines changed: 0 additions & 38 deletions
This file was deleted.

.github/workflows/readmes-updated.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ jobs:
3131
- name: Set up Node.js
3232
uses: actions/setup-node@v3
3333
with:
34-
node-version: 16
34+
node-version: 18
3535
cache: 'npm'
3636
cache-dependency-path: '**/functions/package-lock.json'
3737

.github/workflows/release.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
release:
10+
name: 'Create Releases'
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v3
14+
- name: Release Script
15+
env:
16+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17+
run: |
18+
./.github/workflows/scripts/release.sh

.github/workflows/scripts/release.sh

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
#!/bin/bash
2+
set -e
3+
set -o pipefail
4+
5+
# Uncomment for testing purposes:
6+
7+
#GITHUB_TOKEN=YOUR_TOKEN_HERE
8+
#GITHUB_REPOSITORY=invertase/extensions-release-testing
9+
10+
# -------------------
11+
# Functions
12+
# -------------------
13+
json_escape() {
14+
printf '%s' "$1" | python -c 'import json,sys; print(json.dumps(sys.stdin.read()))'
15+
}
16+
17+
# Creates a new GitHub release
18+
# ARGS:
19+
# 1: Name of the release (becomes the release title on GitHub)
20+
# 2: Markdown body of the release
21+
# 3: Release Git tag
22+
create_github_release() {
23+
local response=''
24+
local release_name=$1
25+
local release_body=$2
26+
local release_tag=$3
27+
28+
local body='{
29+
"tag_name": "%s",
30+
"target_commitish": "master",
31+
"name": "%s",
32+
"body": %s,
33+
"draft": false,
34+
"prerelease": false
35+
}'
36+
37+
# shellcheck disable=SC2059
38+
body=$(printf "$body" "$release_tag" "$release_name" "$release_body")
39+
response=$(curl --request POST \
40+
--url https://api.github.com/repos/${GITHUB_REPOSITORY}/releases \
41+
--header "Authorization: Bearer $GITHUB_TOKEN" \
42+
--header 'Content-Type: application/json' \
43+
--data "$body" \
44+
-s)
45+
46+
created=$(echo "$response" | python -c "import sys, json; data = json.load(sys.stdin); print(data.get('id', sys.stdin))")
47+
if [ "$created" != "$response" ]; then
48+
printf "release created successfully!\n"
49+
else
50+
printf "release failed to create; "
51+
printf "\n%s\n" "$body"
52+
printf "\n%s\n" "$response"
53+
exit 1
54+
fi
55+
}
56+
57+
# Updates an existing GitHub release
58+
# ARGS:
59+
# 1: Name of the release (becomes the release title on GitHub)
60+
# 2: Markdown body of the release
61+
# 3: Release Git tag
62+
# 4: ID of the existing release
63+
update_github_release() {
64+
local response=''
65+
local release_name=$1
66+
local release_body=$2
67+
local release_tag=$3
68+
local release_id=$4
69+
70+
local body='{
71+
"tag_name": "%s",
72+
"target_commitish": "master",
73+
"name": "%s",
74+
"body": %s,
75+
"draft": false,
76+
"prerelease": false
77+
}'
78+
79+
# shellcheck disable=SC2059
80+
body=$(printf "$body" "$release_tag" "$release_name" "$release_body")
81+
response=$(curl --request PATCH \
82+
--url "https://api.github.com/repos/$GITHUB_REPOSITORY/releases/$release_id" \
83+
--header "Authorization: Bearer $GITHUB_TOKEN" \
84+
--header 'Content-Type: application/json' \
85+
--data "$body" \
86+
-s)
87+
88+
updated=$(echo "$response" | python -c "import sys, json; data = json.load(sys.stdin); print(data.get('id', sys.stdin))")
89+
if [ "$updated" != "$response" ]; then
90+
printf "release updated successfully!\n"
91+
else
92+
printf "release failed to update; "
93+
printf "\n%s\n" "$body"
94+
printf "\n%s\n" "$response"
95+
exit 1
96+
fi
97+
}
98+
99+
# Creates or updates a GitHub release
100+
# ARGS:
101+
# 1: Extension name
102+
# 2: Extension version
103+
# 3: Markdown body to use for the release
104+
create_or_update_github_release() {
105+
local response=''
106+
local release_id=''
107+
local extension_name=$1
108+
local extension_version=$2
109+
local release_body=$3
110+
local release_tag="$extension_name-v$extension_version"
111+
local release_name="$extension_name v$extension_version"
112+
113+
response=$(curl --request GET \
114+
--url "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/tags/${release_tag}" \
115+
--header "Authorization: Bearer $GITHUB_TOKEN" \
116+
--header 'Content-Type: application/json' \
117+
--data "$body" \
118+
-s)
119+
120+
release_id=$(echo "$response" | python -c "import sys, json; data = json.load(sys.stdin); print(data.get('id', 'Not Found'))")
121+
if [ "$release_id" != "Not Found" ]; then
122+
existing_release_body=$(echo "$response" | python -c "import sys, json; data = json.load(sys.stdin); print(data.get('body', ''))")
123+
existing_release_body=$(json_escape "$existing_release_body")
124+
# Only update it if the release body is different (this can happen if a change log is manually updated)
125+
printf "Existing release (%s) found for %s - " "$release_id" "$release_tag"
126+
if [ "$existing_release_body" != "$release_body" ]; then
127+
printf "updating it with updated release body ... "
128+
update_github_release "$release_name" "$release_body" "$release_tag" "$release_id"
129+
else
130+
printf "skipping it as release body is already up to date.\n"
131+
fi
132+
else
133+
response_message=$(echo "$response" | python -c "import sys, json; data = json.load(sys.stdin); print(data.get('message'))")
134+
if [ "$response_message" != "Not Found" ]; then
135+
echo "Failed to query release '$release_name' -> GitHub API request failed with response: $response_message"
136+
echo "$response"
137+
exit 1
138+
else
139+
printf "Creating new release '%s' ... " "$release_tag"
140+
create_github_release "$release_name" "$release_body" "$release_tag"
141+
fi
142+
fi
143+
}
144+
145+
# -------------------
146+
# Main Script
147+
# -------------------
148+
149+
# Ensure that the GITHUB_TOKEN env variable is defined
150+
if [[ -z "$GITHUB_TOKEN" ]]; then
151+
echo "Missing required GITHUB_TOKEN env variable. Set this on the workflow action or on your local environment."
152+
exit 1
153+
fi
154+
155+
# Ensure that the GITHUB_REPOSITORY env variable is defined
156+
if [[ -z "$GITHUB_REPOSITORY" ]]; then
157+
echo "Missing required GITHUB_REPOSITORY env variable. Set this on the workflow action or on your local environment."
158+
exit 1
159+
fi
160+
161+
# Find all extensions based on whether a extension.yaml file exists in the directory
162+
for i in $(find . -type f -name 'extension.yaml' -exec dirname {} \; | sort -u); do
163+
# Pluck extension name from directory name
164+
extension_name=$(echo "$i" | sed "s/\.\///")
165+
# Pluck extension latest version from yaml file
166+
extension_version=$(awk '/^version: /' "$i/extension.yaml" | sed "s/version: //")
167+
168+
changelog_contents="No changelog found for this version."
169+
170+
# Ensure changelog exists
171+
if [ -f "$i/CHANGELOG.md" ]; then
172+
# Pluck out change log contents for the latest extension version
173+
changelog_contents=$(awk -v ver="$extension_version" '/^## Version / { if (p) { exit }; if ($3 == ver) { p=1; next} } p && NF' "$i/CHANGELOG.md")
174+
else
175+
echo "WARNING: A changelog could not be found at $i/CHANGELOG.md - a default entry will be used instead."
176+
fi
177+
178+
# JSON escape the markdown content for the release body
179+
changelog_contents=$(json_escape "$changelog_contents")
180+
181+
# Creates a new release if it does not exist
182+
# OR
183+
# Updates an existing release with updated content (allows updating CHANGELOG.md which will update relevant release body)
184+
create_or_update_github_release "$extension_name" "$extension_version" "$changelog_contents"
185+
done

firestore-genai-chatbot/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## Version 0.0.1
2+
3+
Initial release of the firestore-genai-chatbot extension.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
## Testing the extension
2+
3+
You can test out the extension right away by following these steps:
4+
5+
1. Go to the [Cloud Firestore dashboard](https://console.firebase.google.com/project/_/firestore) in the Firebase console.
6+
2. If it doesn't already exist, create the collection you specified during installation: **${param:COLLECTION_NAME}**.
7+
3. Add a document with a **${param:PROMPT_FIELD}** field containing your first message:
8+
9+
```
10+
${param:PROMPT_FIELD}: "How are you today?"
11+
```
12+
13+
4. In a few seconds, you'll see a ${param:ORDER_FIELD} field and then a status field should appear in the document. The status field will update as the extension processes the message.
14+
5. When processing is finished, the ${param:RESPONSE_FIELD} field of the document should be populated with the response from the Google AI Gemini API.
15+
16+
```javascript
17+
const ref = await admin
18+
.firestore()
19+
.collection("${param:COLLECTION_NAME}")
20+
.add({
21+
${param:PROMPT_FIELD}: "How are you today?",
22+
})
23+
24+
ref.onSnapshot(snap => {
25+
if (snap.get('${param:RESPONSE_FIELD}')) console.log(
26+
'RESPONSE:' +
27+
snap.get('${param:RESPONSE_FIELD}')
28+
)
29+
})
30+
```
31+
32+
## About the providers
33+
34+
The extension gives you a choice of what provider to use for the available models:
35+
36+
- Google AI: For more details on this Gemini API, see the [Gemini homepage](https://ai.google.dev/docs).
37+
38+
## About the models
39+
40+
The extension gives you a choice of 2 models:
41+
42+
- [Gemini Pro](https://ai.google.dev/models/gemini) chat model
43+
44+
## Handling errors
45+
46+
If the extension encounters an error, it will write an error message to the document in `status` field. You can use this field to monitor for errors in your documents. Currently some errors will instruct you to visit the Cloud Function logs for the extension, to avoid exposing sensitive information.
47+
48+
## Monitoring
49+
50+
As a best practice, you can [monitor the activity](https://firebase.google.com/docs/extensions/manage-installed-extensions#monitor) of your installed extension, including checks on its health, usage, and logs.

firestore-genai-chatbot/PREINSTALL.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
Use this extension to easily deploy a chatbot using Gemini large language models, stored and managed by Cloud Firestore.
2+
3+
On install you will be asked to provide:
4+
5+
- **Generative AI Provider** This extension makes use of either the Vertex AI Gemini API, or the Google AI Gemini API. To use Google AI you will need to provide a valid API key, Vertex AI will attempt to use Application Default Credentials to authenticate with your Google Cloud Project.
6+
7+
- **Language model**: Which language model do you want to use? Please ensure you pick a model supported by your selected provider.
8+
9+
- **Firestore collection path**: Used to store conversation history represented as documents. This extension will listen to the specified collection(s) for new message documents.
10+
11+
The collection path also supports wildcards, so you can trigger the extension on multiple collections, each with their own private conversation history. This is useful if you want to create separate conversations for different users, or support multiple chat sessions.
12+
13+
Message documents might look like this:
14+
15+
```
16+
{
17+
prompt: “What is the best museum to visit in Barcelona, Spain?”
18+
}
19+
```
20+
21+
When a message document is added, the extension will:
22+
23+
- Obtain conversation history by sorting the documents of the collection.
24+
- Query the language model you selected during configuration.
25+
- Write the message back to the triggering document in a configurable response field.
26+
27+
A createTime field will be automatically created for you on document creation, and will be used to order the conversation history. Gemini, like any other LLM, will have a limited context window, so only the most recent messages will be used as history to generate the next response. Alternatively, If documents in the specified collection already contain a field representing timestamps, you can use that as the order field instead.
28+
29+
You can configure the chatbot to return different responses by providing context during installation. For example, if you want the chatbot to act as a travel guide, you might use this as the context:
30+
31+
```
32+
I want you to act as a travel guide. I will ask you questions about various travel destinations, and you will describe those destinations and give me suggestions on places to visit.
33+
```
34+
35+
You can also configure the model to return different results by tweaking model parameters (temperature, candidate count, etc.), which are exposed as configuration during install as well.
36+
37+
### Choosing a language model
38+
39+
This extension supports the following language models:
40+
41+
- [Gemini Pro](https://ai.google.dev/models/gemini)
42+
43+
### Regenerating a response
44+
45+
Changing the state field of a completed document's status from `COMPLETED` to anything else will retrigger the extension for that document.
46+
47+
## Billing
48+
49+
To install an extension, your project must be on the Blaze (pay as you go) plan. You will be charged a small amount (typically around $0.01/month) for the Firebase resources required by this extension (even if it is not used).
50+
This extension uses other Firebase and Google Cloud Platform services, which have associated charges if you exceed the service’s no-cost tier:
51+
52+
- Cloud Firestore
53+
- Cloud Functions (See [FAQs](https://firebase.google.com/support/faq#extensions-pricing))
54+
55+
[Learn more about Firebase billing.](https://firebase.google.com/pricing)
56+
57+
Additionally, this extension uses the Google AI Gemini API. For more details on this Gemini API, see the [Gemini homepage](https://ai.google.dev/docs).

0 commit comments

Comments
 (0)