Files
firebase-ios-sdk/FirebaseAI/Tests/Unit/TemplateInputTests.swift
Andrew Heard 67debaf689 feat(ai): GA prompt templates API with chat/func calling (#16676)
Promote Server Prompt Templates to General Availability (GA) in the
FirebaseAILogic Swift SDK, enabling developers to decouple prompt
engineering, schemas, and model configs from client release cycles.

Key changes:
- Expose TemplateGenerativeModel publicly via FirebaseAI's
  templateGenerativeModel(tools:toolConfig:requestOptions:).
- Introduce TemplateChat and TemplateGenerativeModel.startChat for
  multi-turn conversations with templates containing {{history}},
  supporting empty turns to run template prompts.
- Add TemplateTool with functionDeclarations and googleMaps, as well
  as TemplateToolConfig for Grounding with Google Maps.
- Add Schema.toJSONSchema() to convert Schema instances to JSON Schema
  Draft 7/2020-12 for function parameter declarations.
- Add TemplateInput conversion supporting primitives, arrays, and
  dictionaries, with NSNumber boolean disambiguation and rich errors.
- Sanitize and percent-encode template IDs in request URLs to prevent
  path and method injection.
- Add comprehensive unit test coverage and end-to-end integration
  tests covering generation, streaming, chat, and function calling.
2026-09-23 17:47:48 -04:00

86 lines
2.9 KiB
Swift

// 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.
@testable import FirebaseAILogic
import XCTest
final class TemplateInputTests: XCTestCase {
func testInitWithFloat() throws {
let floatValue: Float = 3.14
let templateInput = try TemplateInput(value: floatValue)
guard case let .double(doubleValue) = templateInput else {
XCTFail("Expected a .double case, but got \(templateInput)")
return
}
XCTAssertEqual(doubleValue, Double(floatValue), accuracy: 1e-6)
}
// MARK: - NSNumber Bridging
func testInitWithBridgedBoolean() throws {
let templateInput = try TemplateInput(value: NSNumber(value: true))
XCTAssertEqual(templateInput, .bool(true))
}
func testInitWithBridgedInteger() throws {
let templateInput = try TemplateInput(value: NSNumber(value: 1))
XCTAssertEqual(templateInput, .int(1))
}
/// `JSONSerialization` yields `NSNumber`s, where `true` and `1` are mutually castable.
func testInitWithJSONSerializationValues() throws {
let json = Data(#"{"flag": true, "count": 1, "ratio": 1.5}"#.utf8)
let values = try XCTUnwrap(
JSONSerialization.jsonObject(with: json) as? [String: Any]
)
let inputs = try TemplateInput.inputs(from: values)
XCTAssertEqual(inputs["flag"], .bool(true))
XCTAssertEqual(inputs["count"], .int(1))
XCTAssertEqual(inputs["ratio"], .double(1.5))
}
// MARK: - Conversion Errors
func testInputsFromValues_unsupportedValue_throwsErrorNamingKey() throws {
let values: [String: Any] = ["name": "test", "timestamp": Date()]
XCTAssertThrowsError(try TemplateInput.inputs(from: values)) { error in
guard case let EncodingError.invalidValue(_, context) = error else {
XCTFail("Expected EncodingError.invalidValue, got: \(error)")
return
}
XCTAssertEqual(context.codingPath.map(\.stringValue), ["timestamp"])
XCTAssertTrue(
context.debugDescription.contains("timestamp"),
"Expected the variable name in: \(context.debugDescription)"
)
XCTAssertTrue(
context.debugDescription.contains("Date"),
"Expected the offending type in: \(context.debugDescription)"
)
XCTAssertNotNil(context.underlyingError)
}
}
func testInputsFromValues_empty() throws {
let inputs = try TemplateInput.inputs(from: [:])
XCTAssertTrue(inputs.isEmpty)
}
}