Skip to content

Commit 7a2f060

Browse files
authored
Merge branch 'main' into dl/grpc-max-message-size
2 parents c042d03 + 0550af5 commit 7a2f060

59 files changed

Lines changed: 1113 additions & 227 deletions

File tree

Some content is hidden

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

ai-logic/firebase-ai/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Unreleased
22

3+
- [feature] Added automatic function calling support for `LiveGenerativeModel`. (#8223)
4+
35
# 17.13.0
46

57
- [feature] Expanded `SpeechConfig` to support `MultiSpeakerVoiceConfig` and `LanguageCode`.

ai-logic/firebase-ai/src/main/kotlin/com/google/firebase/ai/LiveGenerativeModel.kt

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ import com.google.firebase.FirebaseApp
2020
import com.google.firebase.ai.common.APIController
2121
import com.google.firebase.ai.common.AppCheckHeaderProvider
2222
import com.google.firebase.ai.common.JSON
23+
import com.google.firebase.ai.type.AutoFunctionDeclaration
2324
import com.google.firebase.ai.type.Content
25+
import com.google.firebase.ai.type.FirebaseAutoFunctionException
26+
import com.google.firebase.ai.type.FunctionCallPart
27+
import com.google.firebase.ai.type.FunctionResponsePart
2428
import com.google.firebase.ai.type.GenerativeBackend
2529
import com.google.firebase.ai.type.LiveClientSetupMessage
2630
import com.google.firebase.ai.type.LiveGenerationConfig
@@ -40,8 +44,11 @@ import io.ktor.websocket.readBytes
4044
import kotlin.coroutines.CoroutineContext
4145
import kotlinx.coroutines.channels.ClosedReceiveChannelException
4246
import kotlinx.serialization.ExperimentalSerializationApi
47+
import kotlinx.serialization.InternalSerializationApi
4348
import kotlinx.serialization.encodeToString
4449
import kotlinx.serialization.json.JsonObject
50+
import kotlinx.serialization.json.JsonPrimitive
51+
import kotlinx.serialization.json.jsonObject
4552

4653
/**
4754
* Represents a multimodal model (like Gemini) capable of real-time content generation based on
@@ -140,7 +147,9 @@ internal constructor(
140147
session = webSession,
141148
blockingDispatcher = blockingDispatcher,
142149
firebaseApp = firebaseApp,
143-
connectionFactory = connectFactory
150+
connectionFactory = connectFactory,
151+
hasFunction = ::hasFunction,
152+
executeFunction = ::executeFunction
144153
)
145154
} catch (e: ClosedReceiveChannelException) {
146155
val reason = webSession?.closeReason?.await()
@@ -150,6 +159,55 @@ internal constructor(
150159
}
151160
}
152161

162+
internal fun hasFunction(call: FunctionCallPart): Boolean {
163+
return tools
164+
.flatMap { it.autoFunctionDeclarations ?: emptyList() }
165+
.firstOrNull { it.name == call.name && it.functionReference != null } != null
166+
}
167+
168+
@OptIn(InternalSerializationApi::class)
169+
internal suspend fun executeFunction(call: FunctionCallPart): FunctionResponsePart {
170+
if (tools.isEmpty()) {
171+
throw RuntimeException("No registered tools")
172+
}
173+
val tool = tools.flatMap { it.autoFunctionDeclarations ?: emptyList() }
174+
val declaration =
175+
tool.firstOrNull { it.name == call.name }
176+
?: throw RuntimeException("No registered function named ${call.name}")
177+
return executeFunction<Any, Any>(
178+
call,
179+
declaration as AutoFunctionDeclaration<Any, Any>,
180+
JsonObject(call.args).toString()
181+
)
182+
}
183+
184+
@OptIn(InternalSerializationApi::class)
185+
internal suspend fun <I : Any, O : Any> executeFunction(
186+
functionCall: FunctionCallPart,
187+
functionDeclaration: AutoFunctionDeclaration<I, O>,
188+
parameter: String
189+
): FunctionResponsePart {
190+
val inputDeserializer = functionDeclaration.inputSchema.getSerializer()
191+
val input = JSON.decodeFromString(inputDeserializer, parameter)
192+
val functionReference =
193+
functionDeclaration.functionReference
194+
?: throw RuntimeException("Function reference for ${functionDeclaration.name} is missing")
195+
try {
196+
val output = functionReference.invoke(input)
197+
val outputSerializer = functionDeclaration.outputSchema?.getSerializer()
198+
if (outputSerializer != null) {
199+
return FunctionResponsePart.from(
200+
JSON.encodeToJsonElement(outputSerializer, output).jsonObject
201+
)
202+
.normalizeAgainstCall(functionCall)
203+
}
204+
return (output as FunctionResponsePart).normalizeAgainstCall(functionCall)
205+
} catch (e: FirebaseAutoFunctionException) {
206+
return FunctionResponsePart.from(JsonObject(mapOf("error" to JsonPrimitive(e.message))))
207+
.normalizeAgainstCall(functionCall)
208+
}
209+
}
210+
153211
private companion object {
154212
private val TAG = LiveGenerativeModel::class.java.simpleName
155213
}

ai-logic/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/LiveSession.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,9 @@ internal constructor(
7575
private val firebaseApp: FirebaseApp,
7676
private val connectionFactory:
7777
(suspend (SessionResumptionConfig?) -> DefaultClientWebSocketSession)? =
78-
null
78+
null,
79+
private val hasFunction: ((FunctionCallPart) -> Boolean)? = null,
80+
private val executeFunction: (suspend (FunctionCallPart) -> FunctionResponsePart)? = null
7981
) {
8082
/**
8183
* Coroutine scope that we batch data on for network related behavior.
@@ -580,6 +582,14 @@ internal constructor(
580582
// It's fine to suspend here since you can't have a function call running concurrently
581583
// with an audio response
582584
sendFunctionResponse(it.functionCalls.map(functionCallHandler).toList())
585+
} else if (
586+
hasFunction != null &&
587+
executeFunction != null &&
588+
it.functionCalls.all { f -> hasFunction.invoke(f) }
589+
) {
590+
sendFunctionResponse(
591+
it.functionCalls.map { call -> executeFunction.invoke(call) }.toList()
592+
)
583593
} else {
584594
Log.w(
585595
TAG,

ci/danger/Gemfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ GEM
2525
nap
2626
open4 (~> 1.3)
2727
colored2 (3.1.2)
28-
concurrent-ruby (1.3.6)
28+
concurrent-ruby (1.3.7)
2929
connection_pool (3.0.2)
3030
cork (0.3.0)
3131
colored2 (~> 3.1)
@@ -44,13 +44,13 @@ GEM
4444
pstore (~> 0.1)
4545
terminal-table (>= 1, < 5)
4646
drb (2.2.3)
47-
faraday (2.14.2)
47+
faraday (2.14.3)
4848
faraday-net_http (>= 2.0, < 3.5)
4949
json
5050
logger
5151
faraday-http-cache (2.5.1)
5252
faraday (>= 0.8)
53-
faraday-net_http (3.4.2)
53+
faraday-net_http (3.4.4)
5454
net-http (~> 0.5)
5555
git (2.3.3)
5656
activesupport (>= 5.0)
@@ -59,7 +59,7 @@ GEM
5959
rchardet (~> 1.8)
6060
i18n (1.14.8)
6161
concurrent-ruby (~> 1.0)
62-
json (2.19.5)
62+
json (2.20.0)
6363
kramdown (2.5.1)
6464
rexml (>= 3.3.9)
6565
kramdown-parser-gfm (1.1.0)

encoders/firebase-decoders-json/src/main/java/com/google/firebase/decoders/TypeToken.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
*
2929
* <ol>
3030
* <li>Primitive types
31-
* <li>Array Types: primitive arrays(i.e. {@code int[]}) and object arrays(i.e. {@code Foo[]})
32-
* <li>Plain class types. i.e. {@code Foo}
33-
* <li>Generic types. i.e. {@code Foo<String, Double>}
34-
* <li>Wildcard types: only support {@code Foo<? extend Bar>} by downgrading it to {@code
35-
* Foo<Bar>}, and throw exception when stumbled upon {@code Foo<? super Bar>}.
31+
* <li>Array Types: primitive arrays (like {@code int[]}) and object arrays (like {@code Foo[]})
32+
* <li>Plain class types, for example, {@code Foo}
33+
* <li>Generic types, for example, {@code Foo<String, Double>}
34+
* <li>Wildcard types: only support {@code Foo<? extend Bar>} by downgrading it to
35+
* {@code Foo<Bar>}, and throw exception when stumbled upon {@code Foo<? super Bar>}.
3636
* </ol>
3737
*/
3838
public abstract class TypeToken<T> {
@@ -175,7 +175,7 @@ String getTypeTokenLiteral() {
175175

176176
/**
177177
* {@link ArrayToken} is used to represent Array types in a type-safe manner. such as: primitive
178-
* arrays(i.e. {@code int[]}) and object arrays(i.e. {@code Foo[]})
178+
* arrays (that is, {@code int[]}) and object arrays (that is, {@code Foo[]})
179179
*/
180180
public static class ArrayToken<T> extends TypeToken<T> {
181181
private final TypeToken<?> componentType;

encoders/firebase-encoders-processor/src/main/java/com/google/firebase/encoders/processor/getters/AnnotationProperty.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
/** Represents an annotation property/method with its value as explicitly set in source. */
2121
@AutoValue
2222
public abstract class AnnotationProperty {
23-
/** Name of the property, e.g. \"value\". */
23+
/** Name of the property, for example \"value\". */
2424
public abstract String name();
2525

2626
/** Value of the property. */

encoders/firebase-encoders-processor/src/main/java/com/google/firebase/encoders/processor/getters/Getter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public abstract class Getter {
4545
/**
4646
* Getter's underlying Java type.
4747
*
48-
* <p>The type is fully resolved with respect to generics, i.e. {@code Optional<MyType<String>>},
48+
* <p>The type is fully resolved with respect to generics, for example, {@code Optional<MyType<String>>},
4949
* not {@code Optional<T>}.
5050
*/
5151
public TypeMirror getUnderlyingType() {

encoders/protoc-gen-firebase-encoders/src/main/kotlin/com/google/firebase/encoders/proto/codegen/Types.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,19 @@ sealed class Primitive(override val javaName: String, val defaultValue: String)
5555
* Can be one of [Message] or [ProtoEnum].
5656
*/
5757
sealed class UserDefined : ProtobufType() {
58-
/** A fully qualified protobuf message name, i.e. `com.example.Outer.Inner`. */
58+
/** A fully qualified protobuf message name, for example, `com.example.Outer.Inner`. */
5959
abstract val protobufFullName: String
6060

6161
/**
62-
* Specifies the scope that this type is defined in, i.e. a [Owner.Package] or a parent [Message].
62+
* Specifies the scope that this type is defined in, that is, a [Owner.Package] or a parent
63+
* [Message].
6364
*/
6465
abstract val owner: Owner
6566

6667
/** Unqualified name of this type */
6768
abstract val name: String
6869

69-
/** A fully qualified java name of this type, i.e. `com.example.Outer$Inner`. */
70+
/** A fully qualified java name of this type, for example, `com.example.Outer$Inner`. */
7071
override val javaName: String
7172
get() = "${owner.javaName}${owner.scopeSeparator}$name"
7273

firebase-abt/src/main/java/com/google/firebase/abt/AbtExperimentInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public AbtExperimentInfo(
149149
*
150150
* @param experimentInfoMap A {@link Map} that contains all the keys specified in {@link
151151
* #ALL_REQUIRED_KEYS}. The values of each key must be convertible to the appropriate type,
152-
* e.g., the value for {@link #EXPERIMENT_START_TIME_KEY} must be an ISO 8601 Date string.
152+
* for example, the value for {@link #EXPERIMENT_START_TIME_KEY} must be an ISO 8601 Date string.
153153
* @return An {@link AbtExperimentInfo} with the values of the experiment in {@code
154154
* experimentInfoMap}.
155155
* @throws AbtException If one of the keys is missing, or any of the values cannot be converted to

firebase-crashlytics/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Unreleased
22

3+
- [feature] Added OOM and Anomaly trigger collection for the ProfilingManager API [#8343]
4+
35
# 20.0.6
46

57
- [fixed] Fixed race condition that caused logs from background threads to not be attached to

0 commit comments

Comments
 (0)