Skip to content

Commit

Permalink
Initial Gemini
Browse files Browse the repository at this point in the history
Co-authored-by: Ryan Wilson <[email protected]>
Co-authored-by: Andrew Heard <[email protected]>
Co-authored-by: Peter Friese <[email protected]>
Co-authored-by: Morgan Chen <[email protected]>
  • Loading branch information
5 people committed Dec 12, 2023
1 parent f630729 commit bdcaf73
Show file tree
Hide file tree
Showing 108 changed files with 7,644 additions and 0 deletions.
47 changes: 47 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: check

on:
pull_request:
push:
branches: main

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true

jobs:
check:
runs-on: macos-latest
env:
MINT_PATH: ${{ github.workspace }}/mint
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v4
with:
python-version: 3.6

- name: Cache Mint packages
uses: actions/cache@v3
with:
path: ${{ env.MINT_PATH }}
key: ${{ runner.os }}-mint-${{ hashFiles('**/Mintfile') }}
restore-keys: ${{ runner.os }}-mint-

- name: Setup check
run: |
brew update
brew install mint
mint bootstrap
- name: Style
run: scripts/style.sh test-only

- name: Whitespace
run: scripts/check_whitespace.sh

- name: Filename spaces
run: scripts/check_filename_spaces.sh

- name: Copyrights
run: scripts/check_copyright.sh
30 changes: 30 additions & 0 deletions .github/workflows/cli.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: cli

on:
pull_request:
schedule:
# Run every day at 11pm (PST) - cron uses UTC times
- cron: '0 7 * * *'

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true

jobs:
swift-build-run:
strategy:
matrix:
target: [macOS]
os: [macos-13]
include:
- os: macos-13
xcode: Xcode_15.0.1
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Xcode
run: sudo xcode-select -s /Applications/${{ matrix.xcode }}.app/Contents/Developer
- name: Initialize xcodebuild
run: xcodebuild -list
- name: Build CLI
run: scripts/third_party/travis/retry.sh scripts/build.sh GenerativeAICLI macOS build Examples/GenerativeAICLI
31 changes: 31 additions & 0 deletions .github/workflows/samples.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: samples

on:
pull_request:
schedule:
# Run every day at 11pm (PST) - cron uses UTC times
- cron: '0 7 * * *'

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true

jobs:
swift-build-run:
strategy:
matrix:
# Test build with debug and release configs (whether or not DEBUG is set and optimization level)
build: [build, archive]
os: [macos-13]
include:
- os: macos-13
xcode: Xcode_15.0.1
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Xcode
run: sudo xcode-select -s /Applications/${{ matrix.xcode }}.app/Contents/Developer
- name: Initialize xcodebuild
run: xcodebuild -list
- name: Build the sample
run: scripts/third_party/travis/retry.sh scripts/build.sh GenerativeAISample iOS ${{ matrix.build }} Examples/GenerativeAISample/GenerativeAISample.xcodeproj
30 changes: 30 additions & 0 deletions .github/workflows/spm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: spm

on:
pull_request:
schedule:
# Run every day at 11pm (PST) - cron uses UTC times
- cron: '0 7 * * *'

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
cancel-in-progress: true

jobs:
swift-build-run:
strategy:
matrix:
target: [iOS, macOS]
os: [macos-13]
include:
- os: macos-13
xcode: Xcode_15.0.1
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Xcode
run: sudo xcode-select -s /Applications/${{ matrix.xcode }}.app/Contents/Developer
- name: Initialize xcodebuild
run: xcodebuild -list
- name: Build and unit test
run: scripts/third_party/travis/retry.sh scripts/build.sh generative-ai-swift ${{ matrix.target }} test
37 changes: 37 additions & 0 deletions Examples/Examples/GenerativeAICLI/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// swift-tools-version: 5.9
// The swift-tools-version declares the minimum version of Swift required to build this package.

// Copyright 2023 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.

import PackageDescription

let package = Package(
name: "GenerativeAICLI",
platforms: [.macOS(.v13)],
dependencies: [
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.2.0"),
.package(name: "GoogleGenerativeAI", path: "../../"),
],
targets: [
.executableTarget(
name: "generate-content",
dependencies: [
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "GoogleGenerativeAI", package: "GoogleGenerativeAI"),
],
path: "Sources"
),
]
)
136 changes: 136 additions & 0 deletions Examples/Examples/GenerativeAICLI/Sources/GenerateContent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright 2023 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.

import ArgumentParser
import Foundation
import GoogleGenerativeAI

@main
struct GenerateContent: AsyncParsableCommand {
@Option(help: "The API key to use when calling the Generative Language API.")
var apiKey: String

@Option(name: .customLong("model"), help: "The name of the model to use (e.g., \"gemini-pro\").")
var modelName: String?

@Option(help: "The text prompt for the model in natural language.")
var textPrompt: String?

@Option(
name: .customLong("image-path"),
help: "The file path of an image to pass to the model; must be in JPEG or PNG format.",
transform: URL.filePath(_:)
)
var imageURL: URL?

@Flag(
name: .customLong("streaming"),
help: "Stream response data, printing it incrementally as it's received."
) var isStreaming = false

@Flag(
name: .customLong("GoogleGenerativeAIDebugLogEnabled", withSingleDash: true),
help: "Enable additional debug logging."
) var debugLogEnabled = false

mutating func validate() throws {
if textPrompt == nil && imageURL == nil {
throw ValidationError(
"Missing expected argument(s) '--text-prompt <text-prompt>' and/or" +
" '--image-path <image-path>'."
)
}
}

mutating func run() async throws {
do {
let safetySettings = [SafetySetting(harmCategory: .dangerousContent, threshold: .blockNone)]
// Let the server pick the default config.
let config = GenerationConfig(
temperature: 0.2,
topP: 0.1,
topK: 16,
candidateCount: 1,
maxOutputTokens: isStreaming ? nil : 256,
stopSequences: nil
)

let model = GenerativeModel(
name: modelNameOrDefault(),
apiKey: apiKey,
generationConfig: config,
safetySettings: safetySettings
)

var parts = [ModelContent.Part]()

if let textPrompt = textPrompt {
parts.append(.text(textPrompt))
}

if let imageURL = imageURL {
let mimeType: String
switch imageURL.pathExtension {
case "jpg", "jpeg":
mimeType = "image/jpeg"
case "png":
mimeType = "image/png"
default:
throw CLIError.unsupportedImageType
}
let imageData = try Data(contentsOf: imageURL)
parts.append(.data(mimetype: mimeType, imageData))
}

let input = [ModelContent(parts: parts)]

if isStreaming {
let contentStream = model.generateContentStream(input)
print("Generated Content <streaming>:")
for try await content in contentStream {
if let text = content.text {
print(text)
}
}
} else {
let content = try await model.generateContent(input)
if let text = content.text {
print("Generated Content:\n\(text)")
}
}
} catch {
print("Generate Content Error: \(error)")
}
}

func modelNameOrDefault() -> String {
if let modelName = modelName {
return modelName
} else if imageURL != nil {
return "gemini-pro-vision"
} else {
return "gemini-pro"
}
}
}

enum CLIError: Error {
case unsupportedImageType
}

private extension URL {
static func filePath(_ filePath: String) throws -> URL {
return URL(fileURLWithPath: filePath)
}
}
36 changes: 36 additions & 0 deletions Examples/Examples/GenerativeAISample/APIKey/APIKey.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2023 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.

import Foundation

enum APIKey {
/// Fetch the API key from `GenerativeAI-Info.plist`
/// This is just *one* way how you can retrieve the API key for your app.
static var `default`: String {
guard let filePath = Bundle.main.path(forResource: "GenerativeAI-Info", ofType: "plist")
else {
fatalError("Couldn't find file 'GenerativeAI-Info.plist'.")
}
let plist = NSDictionary(contentsOfFile: filePath)
guard let value = plist?.object(forKey: "API_KEY") as? String else {
fatalError("Couldn't find key 'API_KEY' in 'GenerativeAI-Info.plist'.")
}
if value.starts(with: "_") {
fatalError(
"Follow the instructions at https://ai.google.dev/tutorials/setup to get an API key."
)
}
return value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_KEY</key>
<string>_API_KEY_</string>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"images" : [
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading

0 comments on commit bdcaf73

Please sign in to comment.