Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,15 @@ __pycache__/

# Unencrypted secret files
google-services.json
google-services-desktop.json
GoogleService-Info.plist
uri_prefix.txt
server_key.txt
gcs_key_file.json

# On-device LiteRT-LM model weight files
*.litertlm

# Folders for cmake/test output
*_build/
cmake-build-*/
Expand Down
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ option(FIREBASE_INCLUDE_LIBRARY_DEFAULT
"Should each library be included by default." ON)
# Different options to enable/disable each library being included during
# configuration.
option(FIREBASE_INCLUDE_AI
"Include the Firebase AI Logic library."
${FIREBASE_INCLUDE_LIBRARY_DEFAULT})
option(FIREBASE_INCLUDE_ANALYTICS
"Include the Google Analytics for Firebase library."
${FIREBASE_INCLUDE_LIBRARY_DEFAULT})
Expand Down Expand Up @@ -632,6 +635,9 @@ else()
)
endif()

if (FIREBASE_INCLUDE_AI)
add_subdirectory(ai)
endif()
if (FIREBASE_INCLUDE_ANALYTICS)
add_subdirectory(analytics)
add_dependencies(FIREBASE_GENERATED_HEADERS FIREBASE_ANALYTICS_GENERATED_HEADERS)
Expand Down
265 changes: 265 additions & 0 deletions ai/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# CMake file for the firebase_ai library

# Common C++ source files used across all platforms (Desktop, Android, iOS)
set(common_SRCS
src/common/chat.cc
src/common/firebase_ai.cc
src/common/generative_model.cc
src/common/http_client.cc
src/common/litert_adapter.cc
src/common/litert_c_bridge.cc
src/common/model_content.cc
src/common/serialization.cc
src/common/template_generative_model.cc)

# Slim HTTP sender for Android (JNI -> java.net.HttpURLConnection)
set(android_SRCS
src/android/http_sender_android.cc)

# Slim HTTP sender for iOS (Objective-C++ -> NSURLSession)
set(ios_SRCS
src/ios/http_sender_ios.mm)

# Slim HTTP sender for Desktop / Linux / macOS / Windows (libcurl via firebase_rest_lib)
set(desktop_SRCS
src/desktop/http_sender_desktop.cc)

if(ANDROID)
set(ai_platform_SRCS
"${android_SRCS}")
elseif(IOS)
set(ai_platform_SRCS
"${ios_SRCS}")
else()
set(ai_platform_SRCS
"${desktop_SRCS}")
endif()

if(ANDROID OR IOS)
set(additional_link_LIB)
else()
set(additional_link_LIB
firebase_rest_lib
libcurl)
endif()

add_library(firebase_ai STATIC
${common_SRCS}
${ai_platform_SRCS})

set_property(TARGET firebase_ai PROPERTY FOLDER "Firebase Cpp")

# Set up the dependency on Firebase App.
target_link_libraries(firebase_ai
PUBLIC
firebase_app
PRIVATE
${additional_link_LIB}
)

# Public headers all refer to each other relative to the src/include directory,
# while private headers are relative to the entire C++ SDK directory.
target_include_directories(firebase_ai
PUBLIC
${CMAKE_CURRENT_LIST_DIR}/src/include
PRIVATE
${FIREBASE_CPP_SDK_ROOT_DIR}
${FLATBUFFERS_SOURCE_DIR}/include
${FIREBASE_GEN_FILE_DIR}
${FIREBASE_SPM_SWIFT_HEADERS_DIR}
${CURL_SOURCE_DIR}/include
)

target_compile_definitions(firebase_ai
PRIVATE
-DINTERNAL_EXPERIMENTAL=1
-DFIREBASE_AI_BUILD_LIB_DIR="${CMAKE_CURRENT_BINARY_DIR}"
)

# Automatically download Google AI Edge LiteRT C/C++ SDK and LiteRT-LM runtime
# libraries on a fresh checkout (models are NOT downloaded by CMake).
option(FIREBASE_AI_DOWNLOAD_LITERT
"Automatically download Google AI Edge LiteRT and LiteRT-LM SDKs/runtimes"
ON)
set(FIREBASE_AI_LITERT_VERSION "2.2.0" CACHE STRING "LiteRT release version")
set(FIREBASE_AI_LITERT_LM_VERSION "0.18.0" CACHE STRING "LiteRT-LM release version")

set(FIREBASE_AI_LITERT_DEPS_DIR "${CMAKE_CURRENT_BINARY_DIR}/litert_deps")
if(FIREBASE_AI_DOWNLOAD_LITERT)
file(MAKE_DIRECTORY "${FIREBASE_AI_LITERT_DEPS_DIR}")

# 1. LiteRT C/C++ SDK headers (litert_cc_sdk.zip)
set(LITERT_CC_SDK_ZIP "${FIREBASE_AI_LITERT_DEPS_DIR}/litert_cc_sdk.zip")
set(LITERT_CC_SDK_EXTRACTED "${FIREBASE_AI_LITERT_DEPS_DIR}/litert_cc_sdk")
if(NOT EXISTS "${LITERT_CC_SDK_EXTRACTED}/litert/c/litert_compiled_model.h")
set(LITERT_CC_SDK_URL
"https://github.com/google-ai-edge/LiteRT/releases/download/v${FIREBASE_AI_LITERT_VERSION}/litert_cc_sdk.zip")
message(STATUS "Firebase AI: Downloading LiteRT C/C++ SDK from ${LITERT_CC_SDK_URL}")
file(DOWNLOAD "${LITERT_CC_SDK_URL}" "${LITERT_CC_SDK_ZIP}"
STATUS LITERT_SDK_DL_STATUS)
list(GET LITERT_SDK_DL_STATUS 0 LITERT_SDK_DL_CODE)
if(LITERT_SDK_DL_CODE EQUAL 0)
file(ARCHIVE_EXTRACT INPUT "${LITERT_CC_SDK_ZIP}"
DESTINATION "${FIREBASE_AI_LITERT_DEPS_DIR}")
else()
message(WARNING "Firebase AI: Failed to download LiteRT C/C++ SDK (${LITERT_SDK_DL_STATUS})")
endif()
Comment on lines +118 to +120

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.

medium

If the download of the LiteRT C/C++ SDK fails, CMake currently prints a warning and continues configuration. This will lead to confusing compilation errors later in the build process when headers or libraries are missing. If FIREBASE_AI_DOWNLOAD_LITERT is enabled, a download failure should be treated as a fatal error so the user is notified immediately during the configuration step.

    else()
      message(FATAL_ERROR "Firebase AI: Failed to download LiteRT C/C++ SDK (${LITERT_SDK_DL_STATUS})")
    endif()

endif()
if(NOT DEFINED FIREBASE_AI_LITERT_CC_SDK_DIR AND
EXISTS "${LITERT_CC_SDK_EXTRACTED}/litert/c/litert_compiled_model.h")
set(FIREBASE_AI_LITERT_CC_SDK_DIR "${LITERT_CC_SDK_EXTRACTED}")
endif()

# 2. Platform LiteRT & LiteRT-LM shared runtime libraries
if(APPLE AND NOT IOS)
set(LITERT_DYLIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/libLiteRt.dylib")
if(NOT EXISTS "${LITERT_DYLIB_PATH}")
set(LITERT_DYLIB_URL
"https://storage.googleapis.com/litert/binaries/${FIREBASE_AI_LITERT_VERSION}/macos_arm64/libLiteRt.dylib")
message(STATUS "Firebase AI: Downloading LiteRT runtime from ${LITERT_DYLIB_URL}")
file(DOWNLOAD "${LITERT_DYLIB_URL}" "${LITERT_DYLIB_PATH}"
STATUS LITERT_DYLIB_DL_STATUS)
endif()

set(CLITERTLM_DYLIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/libCLiteRTLM_mac.dylib")
set(CLITERTLM_EXTRACTED_DYLIB
"${FIREBASE_AI_LITERT_DEPS_DIR}/CLiteRTLM_mac.xcframework/macos-arm64_x86_64/libCLiteRTLM_mac.dylib")
if(NOT EXISTS "${CLITERTLM_EXTRACTED_DYLIB}")
set(CLITERTLM_ZIP "${FIREBASE_AI_LITERT_DEPS_DIR}/CLiteRTLM_mac.xcframework.zip")
set(CLITERTLM_URL
"https://github.com/google-ai-edge/LiteRT-LM/releases/download/v${FIREBASE_AI_LITERT_LM_VERSION}/CLiteRTLM_mac.xcframework.zip")
message(STATUS "Firebase AI: Downloading LiteRT-LM macOS runtime from ${CLITERTLM_URL}")
file(DOWNLOAD "${CLITERTLM_URL}" "${CLITERTLM_ZIP}"
STATUS CLITERTLM_DL_STATUS)
list(GET CLITERTLM_DL_STATUS 0 CLITERTLM_DL_CODE)
if(CLITERTLM_DL_CODE EQUAL 0)
file(ARCHIVE_EXTRACT INPUT "${CLITERTLM_ZIP}"
DESTINATION "${FIREBASE_AI_LITERT_DEPS_DIR}")
else()
message(WARNING "Firebase AI: Failed to download LiteRT-LM runtime (${CLITERTLM_DL_STATUS})")
endif()
endif()
if(EXISTS "${CLITERTLM_EXTRACTED_DYLIB}" AND NOT EXISTS "${CLITERTLM_DYLIB_PATH}")
file(COPY_FILE "${CLITERTLM_EXTRACTED_DYLIB}" "${CLITERTLM_DYLIB_PATH}")
endif()
if(EXISTS "${FIREBASE_AI_LITERT_DEPS_DIR}/CLiteRTLM_mac.xcframework/macos-arm64_x86_64/Headers")
target_include_directories(firebase_ai PRIVATE
"${FIREBASE_AI_LITERT_DEPS_DIR}/CLiteRTLM_mac.xcframework/macos-arm64_x86_64/Headers")
endif()
elseif(IOS)
set(CLITERTLM_IOS_DIR "${FIREBASE_AI_LITERT_DEPS_DIR}/CLiteRTLM.xcframework")
if(NOT EXISTS "${CLITERTLM_IOS_DIR}")
set(CLITERTLM_IOS_ZIP "${FIREBASE_AI_LITERT_DEPS_DIR}/CLiteRTLM.xcframework.zip")
set(CLITERTLM_IOS_URL
"https://github.com/google-ai-edge/LiteRT-LM/releases/download/v${FIREBASE_AI_LITERT_LM_VERSION}/CLiteRTLM.xcframework.zip")
message(STATUS "Firebase AI: Downloading LiteRT-LM iOS xcframework from ${CLITERTLM_IOS_URL}")
file(DOWNLOAD "${CLITERTLM_IOS_URL}" "${CLITERTLM_IOS_ZIP}"
STATUS CLITERTLM_IOS_DL_STATUS)
list(GET CLITERTLM_IOS_DL_STATUS 0 CLITERTLM_IOS_DL_CODE)
if(CLITERTLM_IOS_DL_CODE EQUAL 0)
file(ARCHIVE_EXTRACT INPUT "${CLITERTLM_IOS_ZIP}"
DESTINATION "${FIREBASE_AI_LITERT_DEPS_DIR}")
endif()
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(LITERT_SO_PATH "${CMAKE_CURRENT_BINARY_DIR}/libLiteRt.so")
if(NOT EXISTS "${LITERT_SO_PATH}")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64")
set(LITERT_LINUX_ARCH "linux_arm64")
else()
set(LITERT_LINUX_ARCH "linux_x86_64")
endif()
set(LITERT_SO_URL
"https://storage.googleapis.com/litert/binaries/${FIREBASE_AI_LITERT_VERSION}/${LITERT_LINUX_ARCH}/libLiteRt.so")
message(STATUS "Firebase AI: Downloading LiteRT runtime from ${LITERT_SO_URL}")
file(DOWNLOAD "${LITERT_SO_URL}" "${LITERT_SO_PATH}"
STATUS LITERT_SO_DL_STATUS)
endif()
endif()
endif()

if(DEFINED FIREBASE_AI_LITERT_CC_SDK_DIR)
if(EXISTS "${FIREBASE_AI_LITERT_CC_SDK_DIR}/litert/c/litert_compiled_model.h")
target_include_directories(firebase_ai PRIVATE "${FIREBASE_AI_LITERT_CC_SDK_DIR}")
elseif(EXISTS "${FIREBASE_AI_LITERT_CC_SDK_DIR}/include")
target_include_directories(firebase_ai PRIVATE "${FIREBASE_AI_LITERT_CC_SDK_DIR}/include")
target_compile_definitions(firebase_ai PRIVATE -DFIREBASE_AI_USE_LITERT_CC_SDK=1)
if(EXISTS "${FIREBASE_AI_LITERT_CC_SDK_DIR}/lib/libLiteRt.dylib")
target_link_libraries(firebase_ai PRIVATE "${FIREBASE_AI_LITERT_CC_SDK_DIR}/lib/libLiteRt.dylib")
endif()
endif()
endif()

if(ANDROID)
firebase_cpp_proguard_file(ai)
elseif(IOS)
target_compile_options(firebase_ai
PUBLIC $<$<NOT:$<COMPILE_LANGUAGE:Swift>>:-fobjc-arc>)

setup_spm_headers(
firebase_ai
MODULES
FirebaseCore
)

if (FIREBASE_XCODE_TARGET_FORMAT STREQUAL "frameworks")
set_target_properties(firebase_ai PROPERTIES
FRAMEWORK TRUE
)
endif()
endif()

if(NOT IOS)
add_library(firebase_ai_litert_bridge SHARED
src/common/litert_c_bridge.cc)
target_link_libraries(firebase_ai_litert_bridge
PRIVATE
firebase_ai
firebase_app)
if(APPLE)
target_link_libraries(firebase_ai_litert_bridge PRIVATE "-framework Foundation")
endif()
target_include_directories(firebase_ai_litert_bridge
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/src/include
${FIREBASE_CPP_SDK_ROOT_DIR}
${FLATBUFFERS_SOURCE_DIR}/include
)
endif()

if(NOT ANDROID AND NOT IOS)
add_executable(firebase_ai_hybrid_chat
samples/hybrid_chat_main.cc)
target_link_libraries(firebase_ai_hybrid_chat
firebase_ai
firebase_app)
if(APPLE)
target_link_libraries(firebase_ai_hybrid_chat "-framework Foundation")
endif()
target_include_directories(firebase_ai_hybrid_chat
PRIVATE
${CMAKE_CURRENT_LIST_DIR}/src/include
${FIREBASE_CPP_SDK_ROOT_DIR}
)
endif()

if(FIREBASE_CPP_BUILD_TESTS)
add_subdirectory(tests)
endif()

cpp_pack_library(firebase_ai "")
cpp_pack_public_headers()
93 changes: 93 additions & 0 deletions ai/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Firebase AI Logic C++ SDK (`firebase::ai`) — Cloud & LiteRT On-Device Hybrid

This directory implements the `firebase::ai` C++ SDK for **Firebase AI Logic** (Gemini Developer API and Vertex AI Gemini API), including **Hybrid On-Device + Cloud Inference** powered by [Google AI Edge LiteRT](https://developers.google.com/edge/litert/overview#c++_1) and [LiteRT-LM](https://github.com/google-ai-edge/LiteRT-LM).

It also builds `firebase_ai_litert_bridge`, a C ABI shared library used by the **Firebase Unity SDK** (`Firebase.AI`) via P/Invoke so Unity apps can run on-device and hybrid inference through the C++ LiteRT engine while keeping their existing C# Cloud implementation.

---

## Quick Start (Build & Run the Hybrid Multi-Turn Chat Demo)

### 1. Configure & Build via CMake

By default (`FIREBASE_AI_DOWNLOAD_LITERT=ON`), CMake automatically downloads the LiteRT C++ SDK headers (`v2.2.0`), `libLiteRt`, and the `CLiteRTLM` (`v0.18.0`) runtime library during configuration and copies the runtime next to the built binary. Model weights (`.litertlm`) are **not** downloaded automatically.

From the repository root (`firebase-cpp-sdk`):

```bash
cmake -S . -B desktop_build \
-DFIREBASE_INCLUDE_AI=ON \
-DFIREBASE_AI_BUILD_SAMPLES=ON \
-DFIREBASE_AI_BUILD_UNITY_BRIDGE=ON \
-DFIREBASE_CPP_BUILD_TESTS=ON

cmake --build desktop_build \
--target firebase_ai_hybrid_chat firebase_ai_litert_bridge firebase_ai_test -j8
```

### 2. Download a Local Gemma `.litertlm` Model

Download **Gemma 4 E2B IT** (`gemma-4-E2B-it.litertlm` from [`litert-community/gemma-4-E2B-it-litert-lm`](https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm), with a **32k context window**) and place it in `desktop_build/ai/` or pass its path via `--model`:

```bash
curl -L "https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm/resolve/main/gemma-4-E2B-it.litertlm" \
-o desktop_build/ai/gemma-4-E2B-it.litertlm
```

### 3. Provide Your Firebase Configuration (`google-services.json`)

The demo initializes `firebase::App` using a standard Firebase `google-services.json` (or `google-services-desktop.json`) file. Place `google-services.json` in your working directory (or `desktop_build/ai/`), or pass `--config /path/to/google-services.json`.

### 4. Run the Interactive Multi-Turn Hybrid Chat App

```bash
./desktop_build/ai/firebase_ai_hybrid_chat \
--config /path/to/google-services.json \
--model ./desktop_build/ai/gemma-4-E2B-it.litertlm
```

*(If `google-services.json` and `gemma-4-E2B-it.litertlm` are placed in `desktop_build/ai/` or the current directory, they are auto-detected and you can run `./desktop_build/ai/firebase_ai_hybrid_chat` with no arguments.)*

---

## Interactive Chat Commands

Inside `firebase_ai_hybrid_chat`, both Cloud (`gemini-3.1-flash-lite`) and On-Device (`LiteRT-LM`) share a single multi-turn `firebase::ai::Chat` history, so you can switch backends mid-conversation without losing context:

| Command | Description |
| :--- | :--- |
| `/toggle` | Cycle between `ONLY_ON_DEVICE` -> `ONLY_IN_CLOUD` -> `PREFER_ON_DEVICE` -> `PREFER_IN_CLOUD` |
| `/mode local` | Force local on-device inference (`kInferenceModeOnlyOnDevice`) |
| `/mode cloud` | Force cloud Firebase AI inference (`kInferenceModeOnlyInCloud`) |
| `/mode hybrid` | Prefer on-device LiteRT; automatically fall back to Cloud on error (`kInferenceModePreferOnDevice`) |
| `/mode fallback` | Prefer Cloud; automatically fall back to on-device LiteRT when offline (`kInferenceModePreferInCloud`) |
| `/history` | Print the accumulated multi-turn `Chat` history |
| `/compact` | Summarize and compact the conversation history into a concise 2-turn context using the active model |
| `/clear` | Clear the multi-turn `Chat` history |
| `/quit` | Exit the application |

### CLI Flags

- `--model <path>`: Path to a `.litertlm` (LiteRT-LM LLM) or `.tflite` (LiteRT `CompiledModel`) file.
- `--config <path>`: Path to `google-services.json` or `google-services-desktop.json`.
- `--cloud-model <name>`: Cloud Gemini model name (default: `gemini-3.1-flash-lite`).
- `--mode <local|cloud|hybrid|fallback>`: Initial inference mode (default: `hybrid` / `PREFER_ON_DEVICE`).
- `--gpu`: Use GPU acceleration (`kLiteRtAcceleratorGpu`) instead of CPU.
- `--no-stream`: Use non-streaming `Chat::SendMessage` instead of `Chat::SendMessageStream`.
- `--demo`: Run a non-interactive 2-turn verification (Turn 1 on-device -> toggle -> Turn 2 in cloud).

---

## Context Window & Automatic Compaction

- **Auto-Detected Context Length:** When `OnDeviceParams::max_num_tokens` is `0` (the default), `LiteRtAdapter` queries `litert_lm_loaded_file_max_context_tokens` from the `.litertlm` file metadata (`32003` tokens for `gemma-4-E2B-it.litertlm`, `4096` tokens for `gemma3-1b-it-int4.litertlm`, `1024` tokens for `gemma3-270m.litertlm`).
- **Automatic Context Compaction:** Before each on-device turn, `LiteRtAdapter` tokenizes the conversation history via `litert_lm_engine_tokenize`. If the accumulated history exceeds the input token budget, older turns are automatically compacted into `[Compacted Earlier Conversation History]` while keeping recent turns verbatim and reserving headroom for generation output.
- **Repetition Prevention:** On-device generation configures `LiteRtLmRepetitionPenaltyConfig` (`repetition_penalty = 1.15`, `frequency_penalty = 0.25`, `presence_penalty = 0.1`) and `LiteRtLmNoRepeatNgramConfig` (`no_repeat_ngram_size = 4`) so small quantized models do not fall into token repetition loops on long outputs.

---

## Running Unit Tests

```bash
./desktop_build/ai/tests/firebase_ai_test
```
Loading
Loading