Skip to content
Merged
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
3 changes: 2 additions & 1 deletion firebase-ai/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Unreleased

- [changed] **Breaking Change**: Removed the `candidateCount` option from `LiveGenerationConfig`
- [changed] Added support for the URL context tool, which allows the model to access content from provided public web URLs to inform and enhance its responses.
- [changed] Added support for the URL context tool, which allows the model to access content from
provided public web URLs to inform and enhance its responses.

# 17.3.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,17 @@ import kotlinx.serialization.json.JsonNames
* enabled.
*/
public class Candidate
@OptIn(PublicPreviewAPI::class)
internal constructor(
public val content: Content,
public val safetyRatings: List<SafetyRating>,
public val citationMetadata: CitationMetadata?,
public val finishReason: FinishReason?,
public val groundingMetadata: GroundingMetadata?,
public val urlContextMetadata: UrlContextMetadata?
@property:PublicPreviewAPI public val urlContextMetadata: UrlContextMetadata?
) {

@OptIn(PublicPreviewAPI::class)
@Serializable
internal data class Internal(
val content: Content.Internal? = null,
Expand All @@ -56,6 +58,8 @@ internal constructor(
val groundingMetadata: GroundingMetadata.Internal? = null,
val urlContextMetadata: UrlContextMetadata.Internal? = null
) {

@OptIn(PublicPreviewAPI::class)
internal fun toPublic(): Candidate {
val safetyRatings = safetyRatings?.mapNotNull { it.toPublic() }.orEmpty()
val citations = citationMetadata?.toPublic()
Expand Down Expand Up @@ -505,7 +509,9 @@ public class Segment(
*/
@PublicPreviewAPI
public class UrlContextMetadata internal constructor(public val urlMetadata: List<UrlMetadata>) {

@Serializable
@PublicPreviewAPI
internal data class Internal(val urlMetadata: List<UrlMetadata.Internal>?) {
internal fun toPublic() = UrlContextMetadata(urlMetadata?.map { it.toPublic() } ?: emptyList())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,9 @@ package com.google.firebase.ai.type
"This API is part of an experimental public preview and may change in " +
"backwards-incompatible ways without notice.",
)
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY
)
public annotation class PublicPreviewAPI()
10 changes: 9 additions & 1 deletion firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Tool.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,24 @@ import kotlinx.serialization.json.JsonObject
* can be used to gather information or complete tasks.
*/
public class Tool
@OptIn(PublicPreviewAPI::class)
internal constructor(
internal val functionDeclarations: List<FunctionDeclaration>?,
internal val googleSearch: GoogleSearch?,
internal val codeExecution: JsonObject?,
internal val urlContext: UrlContext?,
@property:PublicPreviewAPI internal val urlContext: UrlContext?,
) {

@OptIn(PublicPreviewAPI::class)
internal fun toInternal() =
Internal(
functionDeclarations?.map { it.toInternal() } ?: emptyList(),
googleSearch = this.googleSearch?.toInternal(),
codeExecution = this.codeExecution,
urlContext = this.urlContext?.toInternal()
)

@OptIn(PublicPreviewAPI::class)
@Serializable
internal data class Internal(
val functionDeclarations: List<FunctionDeclaration.Internal>? = null,
Expand All @@ -47,6 +52,7 @@ internal constructor(
)
public companion object {

@OptIn(PublicPreviewAPI::class)
private val codeExecutionInstance by lazy { Tool(null, null, JsonObject(emptyMap()), null) }

/**
Expand All @@ -56,6 +62,7 @@ internal constructor(
*/
@JvmStatic
public fun functionDeclarations(functionDeclarations: List<FunctionDeclaration>): Tool {
@OptIn(PublicPreviewAPI::class)
return Tool(functionDeclarations, null, null, null)
}

Expand Down Expand Up @@ -97,6 +104,7 @@ internal constructor(
*/
@JvmStatic
public fun googleSearch(googleSearch: GoogleSearch = GoogleSearch()): Tool {
@OptIn(PublicPreviewAPI::class)
return Tool(null, googleSearch, null, null)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.google.firebase.ai

import com.google.firebase.ai.type.FinishReason
import com.google.firebase.ai.type.InvalidAPIKeyException
import com.google.firebase.ai.type.PublicPreviewAPI
import com.google.firebase.ai.type.ResponseStoppedException
import com.google.firebase.ai.type.ServerException
import com.google.firebase.ai.type.UrlRetrievalStatus
Expand Down Expand Up @@ -135,6 +136,7 @@ internal class DevAPIUnarySnapshotTests {
}
}

@OptIn(PublicPreviewAPI::class)
@Test
fun `url context`() =
goldenDevAPIUnaryFile("unary-success-url-context.json") {
Expand Down Expand Up @@ -167,6 +169,7 @@ internal class DevAPIUnarySnapshotTests {
}
}

@OptIn(PublicPreviewAPI::class)
@Test
fun `url context mixed validity`() =
goldenDevAPIUnaryFile("unary-success-url-context-mixed-validity.json") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.google.firebase.ai.common.util.doBlocking
import com.google.firebase.ai.type.Candidate
import com.google.firebase.ai.type.Content
import com.google.firebase.ai.type.GenerateContentResponse
import com.google.firebase.ai.type.PublicPreviewAPI
import com.google.firebase.ai.type.RequestOptions
import com.google.firebase.ai.type.ServerException
import com.google.firebase.ai.type.TextPart
Expand All @@ -41,7 +42,6 @@ import io.ktor.http.content.TextContent
import io.ktor.http.headersOf
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.withTimeout
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.encodeToString
import org.junit.Before
import org.junit.Test
Expand Down Expand Up @@ -146,7 +146,7 @@ internal class GenerativeModelTesting {
exception.message shouldContain "location"
}

@OptIn(ExperimentalSerializationApi::class)
@OptIn(PublicPreviewAPI::class)
private fun generateContentResponseAsJsonString(text: String): String {
return JSON.encodeToString(
GenerateContentResponse.Internal(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import com.google.firebase.ai.type.Content
import com.google.firebase.ai.type.CountTokensResponse
import com.google.firebase.ai.type.FunctionCallingConfig
import com.google.firebase.ai.type.GoogleSearch
import com.google.firebase.ai.type.PublicPreviewAPI
import com.google.firebase.ai.type.RequestOptions
import com.google.firebase.ai.type.TextPart
import com.google.firebase.ai.type.Tool
Expand Down Expand Up @@ -285,6 +286,7 @@ internal class RequestFormatTests {
)

withTimeout(5.seconds) {
@OptIn(PublicPreviewAPI::class)
controller
.generateContentStream(
GenerateContentRequest(
Expand Down Expand Up @@ -323,6 +325,7 @@ internal class RequestFormatTests {
)

withTimeout(5.seconds) {
@OptIn(PublicPreviewAPI::class)
controller
.generateContentStream(
GenerateContentRequest(
Expand Down Expand Up @@ -432,6 +435,7 @@ internal class RequestFormatTests {
)

withTimeout(5.seconds) {
@OptIn(PublicPreviewAPI::class)
controller
.generateContentStream(
GenerateContentRequest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.google.firebase.ai.common.JSON
import com.google.firebase.ai.type.Candidate
import com.google.firebase.ai.type.Content
import com.google.firebase.ai.type.GenerateContentResponse
import com.google.firebase.ai.type.PublicPreviewAPI
import com.google.firebase.ai.type.RequestOptions
import com.google.firebase.ai.type.TextPart
import io.ktor.client.engine.mock.MockEngine
Expand All @@ -32,7 +33,6 @@ import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.headersOf
import io.ktor.utils.io.ByteChannel
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.encodeToString
import org.mockito.Mockito

Expand All @@ -44,7 +44,7 @@ internal fun prepareStreamingResponse(
response: List<GenerateContentResponse.Internal>
): List<ByteArray> = response.map { "data: ${JSON.encodeToString(it)}$SSE_SEPARATOR".toByteArray() }

@OptIn(ExperimentalSerializationApi::class)
@OptIn(PublicPreviewAPI::class)
internal fun createResponses(vararg text: String): List<GenerateContentResponse.Internal> {
val candidates =
text.map { Candidate.Internal(Content.Internal(parts = listOf(TextPart.Internal(it)))) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ internal class ToolTest {
tool.googleSearch.shouldNotBeNull()
tool.functionDeclarations.shouldBeNull()
tool.codeExecution.shouldBeNull()
@OptIn(PublicPreviewAPI::class)
tool.urlContext.shouldBeNull()
}

Expand All @@ -39,6 +40,7 @@ internal class ToolTest {
tool.functionDeclarations?.first() shouldBe functionDeclaration
tool.googleSearch.shouldBeNull()
tool.codeExecution.shouldBeNull()
@OptIn(PublicPreviewAPI::class)
tool.urlContext.shouldBeNull()
}

Expand All @@ -48,16 +50,19 @@ internal class ToolTest {
tool.codeExecution.shouldNotBeNull()
tool.functionDeclarations.shouldBeNull()
tool.googleSearch.shouldBeNull()
@OptIn(PublicPreviewAPI::class)
tool.urlContext.shouldBeNull()
}

@OptIn(PublicPreviewAPI::class)
@Test
fun `urlContext() creates a tool with a urlContext property`() {
val tool = Tool.urlContext()

tool.googleSearch.shouldBeNull()
tool.functionDeclarations.shouldBeNull()
tool.codeExecution.shouldBeNull()
@OptIn(PublicPreviewAPI::class)
tool.urlContext.shouldNotBeNull()
}
}
Loading