Files
Andrew Heard 3796a55fdf feat(ai): add GeminiLanguageModel (#16601)
Implemented `GeminiLanguageModel`, providing an adapter for Google's
Gemini models conforming to Apple's `FoundationModels.LanguageModel`
protocol. This PR includes text to text generation capabilities; other
modalities and Gemini features will be handled in follow-up PRs.

**Note:** The `GeminiLanguageModel` implementation is guarded by the
environment variable `GEMINI_LANGUAGE_MODEL`. By default none of the
`GeminiLanguageModel` implementation is included.

Key additions:
- `GeminiLanguageModel`: Implements `LanguageModel` protocol with
  reasoning capabilities, configuring execution via `ModelResource`
  and `EndpointConfiguration`.
- `GeminiLanguageModelExecutor`: Converts prompt instructions and
  history into Gemini streaming requests, yielding responses as
  `LanguageModelSession.ResponseStream`.
- `GeminiTranscriptTranslator`: Translates Apple `Transcript` entries
  and segments into Gemini `Content` structures while preserving thought
  signatures and validating supported transcript features.
- `GeminiLanguageModel.Error` and `GeminiErrorMapper`: Translates Gemini
  API, HTTP, and network errors into standard `LanguageModelError` cases
  (`rateLimited`, `timeout`, `guardrailViolation`, `refusal`, and
  `unsupportedLanguageOrLocale`) or `GeminiLanguageModel.Error` cases
  (`serviceUnavailable`, `networkFailure`, `modelNotFound`, `apiError`).
  The `APIError` payload is designed for seamless forward compatibility
  with both GenerateContent and the upcoming Interactions API.
- Testing infrastructure: Adds `ConditionTrait.requireFoundationModels`
  to conditionally run Foundation Models tests only on supported
  platforms (macOS 27+, iOS 27+, watchOS 27+, visionOS 27+), along with
  shared test constants, mock fixtures, and comprehensive unit and
  integration test suites.
2026-09-04 18:05:45 -04:00

210 lines
6.1 KiB
Bash
Executable File

#!/bin/bash
# Copyright 2017 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.
# Usage:
# ./scripts/style.sh [branch-name | filenames | -b]
#
# With no arguments, formats all eligible files in the repo
# Pass -b (or --branch) to format all files changed on the current branch (all commits + uncommitted changes)
# Pass a branch name to format all eligible files changed since that branch
# Pass a specific file or directory name to format just files found there
#
# Commonly
# ./scripts/style.sh -b
# ./scripts/style.sh main
# Set the environment variable FIR_CLANG_FORMAT_PATH to use a specific version
# of clang-format, regardless of its order in the shell PATH.
#
# Example (add to ~/.bash_profile, ~/.zshrc or run in interactive shell):
# FIR_CLANG_FORMAT_PATH="$(brew --prefix clang-format)/bin/clang-format"
# export FIR_CLANG_FORMAT_PATH
if [[ -n "$FIR_CLANG_FORMAT_PATH" ]]; then
clang_format_bin="$FIR_CLANG_FORMAT_PATH"
elif ! clang_format_bin=$(command -v clang-format); then
echo "clang-format not found, install with 'brew install clang-format'"
exit 1
fi
# Strip the clang-format version output down to the major version. Examples:
# clang-format version 7.0.0 (tags/google/stable/2018-01-11)
# clang-format version google3-trunk (trunk r333779)
version=$("$clang_format_bin" --version)
# Log the version in non-interactive use as it can be useful in travis logs.
if [[ ! -t 1 ]]; then
echo "Clang-format Path: $clang_format_bin"
echo "Clang-format Version: $version"
fi
# Remove leading "clang-format version"
version="${version/*version /}"
# Remove trailing parenthetical version details
version="${version/ (*)/}"
# Remove everything after the first dot (note this is a glob, not a regex)
version="${version/.*/}"
case "$version" in
23)
;;
google3-trunk)
echo "Please use a publicly released clang-format; a recent LLVM release"
echo "from http://releases.llvm.org/download.html will work."
echo "Prepend to PATH when running style.sh."
exit 1
;;
*)
echo "Please upgrade to clang-format version 23."
echo "If it's installed via homebrew you can run:"
echo "brew upgrade clang-format"
exit 1
;;
esac
# Ensure that tools in `Mintfile` are installed locally to avoid permissions
# problems that would otherwise arise from the default of installing in
# /usr/local.
export MINT_PATH=Mint
if ! which mint >/dev/null 2>&1; then
echo "mint is not available, install with 'brew install mint'"
exit 1
fi
system=$(uname -s)
# Joins the given arguments with the separator given as the first argument.
function join() {
local IFS="$1"
shift
echo "$*"
}
clang_options=(-style=file)
# Swift formatting options for the repo should be configured in
# https://github.com/firebase/firebase-ios-sdk/blob/main/.swiftformat.
# These may be overridden with additional `.swiftformat` files in subdirectories.
swift_options=()
if [[ $# -gt 0 && "$1" == "test-only" ]]; then
test_only=true
clang_options+=(-output-replacements-xml)
swift_options+=(--dryrun)
shift
else
test_only=false
clang_options+=(-i)
fi
#TODO(#2223) - Find a way to handle spaces in filenames
files=$(
(
if [[ $# -gt 0 ]]; then
if [[ "$1" == "-b" || "$1" == "--branch" ]]; then
merge_base=$(git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main 2>/dev/null || echo "main")
git diff --name-only --relative --diff-filter=ACMR "$merge_base"
elif git rev-parse "$1" -- >& /dev/null; then
# Argument was a branch name show files changed since that branch
git diff --name-only --relative --diff-filter=ACMR "$1"
else
# Otherwise assume the passed things are files or directories
find "$@" -type f
fi
else
# Do everything by default
if [[ -t 2 && "$test_only" == false ]]; then
echo "Formatting all eligible files... (Tip: run './scripts/style.sh -b' to format only files changed on this branch)" >&2
fi
find . -type f
fi
) | sed -E -n '
# find . includes a leading "./" that git does not include
s%^./%%
# Build outputs
\%/Pods/% d
\%^build/% d
\%^Debug/% d
\%^Release/% d
\%^cmake-build-debug/% d
\%^.build/% d
\%^.swiftpm/% d
# pod gen output
\%^gen/% d
# Sources controlled outside this tree
\%/third_party/% d
# Public headers for closed sourced FirebaseAnalytics
\%^(FirebaseAnalyticsWrapper)/% d
# Generated source
\%/Firestore/core/src/util/config.h% d
# Sources pulled in by the Mint package manager
\%^mint% d
# Keep Firebase.h indenting
\%^CoreOnly/Sources/Firebase.h% d
# Checked-in generated code
\%\.pb(objc|rpc)\.% d
\%\.pb\.% d
\%\.nanopb\.% d
# GeminiLanguageModel sources
\%^GeminiLanguageModel% d
# Format C-ish sources and shell scripts only
\%\.(h|m|mm|cc|swift|sh)$% p
'
)
needs_formatting=false
for f in $files; do
if [[ "${f: -6}" == '.swift' ]]; then
# Match output that says:
# 1/1 files would have been formatted. (with --dryrun)
# 1/1 files formatted. (without --dryrun)
mint run swiftformat "${swift_options[@]}" "$f" 2>&1 | grep '^1/1 files' > /dev/null
elif [[ "${f: -3}" == '.sh' ]]; then
if [[ "$test_only" == true ]]; then
grep -E '[[:space:]]+$' "$f" > /dev/null
else
if [[ "$(uname -s)" == "Darwin" ]]; then
sed -i '' -E 's/[[:space:]]+$//' "$f"
else
sed -i -E 's/[[:space:]]+$//' "$f"
fi
fi
else
"$clang_format_bin" "${clang_options[@]}" "$f" | grep "<replacement " > /dev/null
fi
if [[ "$test_only" == true && $? -ne 1 ]]; then
echo "$f needs formatting."
needs_formatting=true
fi
done
if [[ "$needs_formatting" == true ]]; then
echo "Proposed commit is not style compliant."
echo "Run scripts/style.sh and git add the result."
exit 1
fi