first commit

This commit is contained in:
sitky 2026-06-03 11:47:59 +07:00
commit 0c76bc6b2b
178 changed files with 11170 additions and 0 deletions

View File

@ -0,0 +1,305 @@
#!/usr/bin/env python3
"""
AC Control System untuk AC Panasonic
Mengontrol AC berdasarkan deteksi kehadiran (AUTO) atau manual via MQTT
"""
import paho.mqtt.client as mqtt
from paho.mqtt import client as mqtt_client
import subprocess
import os
import json
import time
import signal
import sys
import threading
from datetime import datetime
# ====================== KONFIGURASI ======================
MQTT_BROKER = "localhost"
MQTT_PORT = 1883
CONTROL_TOPIC = "classroom/ac/control"
STATUS_TOPIC = "classroom/ac/status"
PRESENCE_TOPIC = "classroom/presence"
# GPIO untuk IR
TRANSMITTER_GPIO = 18
# File konfigurasi
IRRP_SCRIPT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "irrp.py")
JSON_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ac_codes.json")
LOG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "ac_control.log")
# State variables
AUTO_MODE = True
CURRENT_AC_STATE = "off"
# Timer Delay
DELAY_TIMER_MINUTES = 5
DELAY_TIMER_ACTIVE = False
DELAY_TIMER_THREAD = None
# ====================== LOGGING ======================
def log_message(message, level="INFO"):
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_entry = f"[{timestamp}] [{level}] {message}"
print(log_entry)
try:
with open(LOG_FILE, "a") as f:
f.write(log_entry + "\n")
except:
pass
# ====================== FUNGSI KIRIM STATUS ======================
def publish_status(client, reason=""):
"""Kirim status AC ke MQTT"""
status_message = {
"ac_state": CURRENT_AC_STATE,
"auto_mode": AUTO_MODE,
"delay_active": DELAY_TIMER_ACTIVE,
"delay_remaining": DELAY_TIMER_MINUTES * 60 if DELAY_TIMER_ACTIVE else 0,
"reason": reason,
"timestamp": datetime.now().isoformat()
}
client.publish(STATUS_TOPIC, json.dumps(status_message))
log_message(f"📤 Status dikirim: {CURRENT_AC_STATE} (reason: {reason})")
# ====================== IR CONTROL ======================
def send_ir(key):
log_message(f"Mengirim IR: {key}")
if not os.path.exists(JSON_FILE):
log_message(f"File {JSON_FILE} tidak ditemukan!", "ERROR")
return False
try:
with open(JSON_FILE, 'r') as f:
codes = json.load(f)
if key not in codes:
log_message(f"Key '{key}' tidak ditemukan di JSON", "ERROR")
return False
except Exception as e:
log_message(f"Gagal baca JSON: {e}", "ERROR")
return False
cmd = ["python3", IRRP_SCRIPT, "-p", "-g", str(TRANSMITTER_GPIO), "-f", JSON_FILE, key]
try:
subprocess.run(cmd, check=True, capture_output=True, text=True, timeout=10)
log_message(f"IR berhasil dikirim: {key}")
return True
except Exception as e:
log_message(f"Gagal kirim IR: {e}", "ERROR")
return False
def set_ac_state(state, client=None, reason=""):
global CURRENT_AC_STATE
if state == "on" and CURRENT_AC_STATE == "off":
log_message("Menyalakan AC...")
if send_ir("POWER_ON"):
CURRENT_AC_STATE = "on"
if client:
publish_status(client, reason or "power_on")
return True
return False
elif state == "off" and CURRENT_AC_STATE == "on":
log_message("Mematikan AC...")
if send_ir("POWER_OFF"):
CURRENT_AC_STATE = "off"
if client:
publish_status(client, reason or "power_off")
return True
return False
return False
# ====================== DELAY TIMER ======================
def start_delay_timer(client):
global DELAY_TIMER_ACTIVE, DELAY_TIMER_THREAD
if DELAY_TIMER_ACTIVE:
log_message("Delay timer sudah aktif")
return
DELAY_TIMER_ACTIVE = True
log_message(f"⏳ Delay timer dimulai: {DELAY_TIMER_MINUTES} menit")
# Kirim status timer aktif
publish_status(client, "delay_started")
def timer_worker():
global DELAY_TIMER_ACTIVE, CURRENT_AC_STATE
total_seconds = DELAY_TIMER_MINUTES * 60
for remaining in range(total_seconds, -1, -1):
if not DELAY_TIMER_ACTIVE:
log_message("Delay timer dibatalkan")
return
if remaining > 0:
# Update setiap 10 detik
if remaining % 10 == 0 or remaining <= 5:
status_msg = {
"ac_state": CURRENT_AC_STATE,
"auto_mode": AUTO_MODE,
"delay_active": True,
"delay_remaining": remaining
}
client.publish(STATUS_TOPIC, json.dumps(status_msg))
minutes = remaining // 60
seconds = remaining % 60
log_message(f"Sisa waktu: {minutes:02d}:{seconds:02d}")
time.sleep(1)
else:
# Timer habis
if DELAY_TIMER_ACTIVE and CURRENT_AC_STATE == "on":
log_message("⏰ TIMER HABIS - Mematikan AC")
if send_ir("POWER_OFF"):
CURRENT_AC_STATE = "off"
publish_status(client, "delay_timer_expired")
DELAY_TIMER_ACTIVE = False
return
DELAY_TIMER_THREAD = threading.Thread(target=timer_worker, daemon=True)
DELAY_TIMER_THREAD.start()
def cancel_delay_timer(client):
global DELAY_TIMER_ACTIVE
if DELAY_TIMER_ACTIVE:
DELAY_TIMER_ACTIVE = False
log_message("Delay timer dibatalkan")
publish_status(client, "delay_cancelled")
# ====================== MQTT HANDLERS ======================
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code == 0:
log_message("✅ MQTT terhubung ke broker")
client.subscribe([(CONTROL_TOPIC, 0), (PRESENCE_TOPIC, 0)])
log_message(f"Subscribed ke: {CONTROL_TOPIC} & {PRESENCE_TOPIC}")
# Kirim status awal
publish_status(client, "initial_connection")
else:
log_message(f"❌ MQTT connect gagal: {reason_code}", "ERROR")
def on_message(client, userdata, msg):
global AUTO_MODE, CURRENT_AC_STATE, DELAY_TIMER_MINUTES
topic = msg.topic
payload = msg.payload.decode().strip()
log_message(f"MQTT → {topic} : {payload}")
# ==================== PRESENCE (AUTO MODE) ====================
if topic == PRESENCE_TOPIC:
if not AUTO_MODE:
log_message("Mode MANUAL aktif → ignore presence")
return
presence = payload.lower()
if presence == "ada":
log_message("👥 Ada orang terdeteksi (AUTO)")
cancel_delay_timer(client)
if CURRENT_AC_STATE == "off":
log_message("🔛 Menyalakan AC otomatis...")
set_ac_state("on", client, "presence_detected")
elif presence == "tidak ada":
log_message("🚪 Tidak ada orang (AUTO)")
if CURRENT_AC_STATE == "on" and not DELAY_TIMER_ACTIVE:
log_message(f"⏳ Memulai delay timer {DELAY_TIMER_MINUTES} menit...")
start_delay_timer(client)
# ==================== CONTROL (DARI WEB) ====================
elif topic == CONTROL_TOPIC:
payload_lower = payload.lower()
if payload_lower == "auto_on":
AUTO_MODE = True
cancel_delay_timer(client)
log_message("🤖 Mode AUTO diaktifkan")
publish_status(client, "auto_mode_on")
elif payload_lower == "auto_off":
AUTO_MODE = False
cancel_delay_timer(client)
log_message("👤 Mode MANUAL diaktifkan")
publish_status(client, "manual_mode_on")
elif payload_lower.startswith("delay_"):
try:
minutes = int(payload_lower.split("_")[1])
if 1 <= minutes <= 60:
DELAY_TIMER_MINUTES = minutes
log_message(f"⚙️ Delay timer diubah menjadi {minutes} menit")
publish_status(client, "delay_changed")
except:
pass
elif payload_lower == "on":
log_message("👤 Manual: Menyalakan AC")
cancel_delay_timer(client)
set_ac_state("on", client, "manual_on")
elif payload_lower == "off":
log_message("👤 Manual: Mematikan AC")
cancel_delay_timer(client)
set_ac_state("off", client, "manual_off")
elif payload_lower == "status":
publish_status(client, "manual_request")
# ====================== SIGNAL HANDLER ======================
def signal_handler(sig, frame):
log_message("Menerima sinyal shutdown...")
sys.exit(0)
# ====================== MAIN ======================
if __name__ == "__main__":
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
log_message("=" * 70)
log_message("🚀 AC CONTROL SYSTEM - PANASONIC")
log_message("=" * 70)
# Tampilkan kode IR yang tersedia
if os.path.exists(JSON_FILE):
try:
with open(JSON_FILE, 'r') as f:
codes = json.load(f)
log_message(f"Kode IR tersedia: {list(codes.keys())}")
except Exception as e:
log_message(f"Gagal baca ac_codes.json: {e}", "ERROR")
else:
log_message(f"⚠️ File {JSON_FILE} tidak ditemukan!", "WARNING")
log_message(" Jalankan record terlebih dahulu: python3 rcd.py POWER_ON")
# Status awal
log_message(f"Mode awal : {'AUTO' if AUTO_MODE else 'MANUAL'}")
log_message(f"AC : {CURRENT_AC_STATE}")
log_message(f"Delay Timer : {DELAY_TIMER_MINUTES} menit")
log_message(f"Log file : {LOG_FILE}")
# MQTT Client
try:
client = mqtt.Client(mqtt_client.CallbackAPIVersion.VERSION2)
client.on_connect = on_connect
client.on_message = on_message
client.connect(MQTT_BROKER, MQTT_PORT, 60)
log_message("\n✅ Sistem siap. Menunggu perintah MQTT...")
log_message(f" Subscribe ke: {CONTROL_TOPIC} & {PRESENCE_TOPIC}")
log_message(" Tekan Ctrl+C untuk berhenti\n")
client.loop_forever()
except Exception as e:
log_message(f"❌ Error MQTT: {e}", "ERROR")
sys.exit(1)

View File

@ -0,0 +1,238 @@
#!/usr/bin/env python3
import cv2
import numpy as np
import paho.mqtt.client as mqtt
from paho.mqtt import client as mqtt_client
import time
import base64
import threading
import json
import subprocess
import os
from datetime import datetime
# ====================== KONFIGURASI ======================
MQTT_BROKER = "localhost"
MQTT_PORT = 1883
PRESENCE_TOPIC = "classroom/presence"
CONTROL_TOPIC = "classroom/ac/control"
WEBCAM_TOPIC = "classroom/webcam"
STATUS_TOPIC = "classroom/ac/status"
WEIGHTS = "yolov4-tiny.weights"
CFG = "yolov4-tiny.cfg"
NAMES = "coco.names"
CONFIDENCE_THRESHOLD = 0.5
INPUT_SIZE = (256, 256)
WEBCAM_INTERVAL = 2
WEBCAM_QUALITY = 60
WEBCAM_WIDTH = 640
WEBCAM_HEIGHT = 480
# ========================================================
print("=" * 60)
print("🚀 AC CONTROL SYSTEM - DENGAN WEBCAM MONITORING")
print("=" * 60)
# ====================== CLEANUP WEBCAM ======================
print("Membersihkan proses yang menggunakan webcam...")
try:
# Kill semua proses yang menggunakan webcam
subprocess.run(["sudo", "fuser", "-k", "/dev/video0"], capture_output=True)
time.sleep(1)
except:
pass
# ====================== INISIALISASI WEBCAM ======================
print("Membuka webcam...")
cap = None
# Coba buka webcam dengan berbagai metode
for i in range(3): # Coba 3 kali
cap = cv2.VideoCapture(0, cv2.CAP_V4L2)
if cap.isOpened():
print(f"✅ Webcam terbuka pada percobaan ke-{i+1}")
break
time.sleep(1)
if cap is None or not cap.isOpened():
print("❌ Gagal membuka webcam!")
print("\nTroubleshooting:")
print("1. Cek koneksi USB webcam")
print("2. Jalankan: sudo fuser -k /dev/video0")
print("3. Restart: sudo reboot")
exit(1)
# Set properti webcam
cap.set(cv2.CAP_PROP_FRAME_WIDTH, WEBCAM_WIDTH)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, WEBCAM_HEIGHT)
cap.set(cv2.CAP_PROP_FPS, 30)
# Test baca frame
ret, test_frame = cap.read()
if not ret:
print("❌ Webcam terbuka tapi gagal membaca frame!")
cap.release()
exit(1)
print(f"✅ Webcam siap: {int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))}x{int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))}")
# ====================== LOAD MODEL YOLO ======================
print("Memuat model YOLOv4-tiny...")
for file in [WEIGHTS, CFG, NAMES]:
if not os.path.exists(file):
print(f"❌ File {file} tidak ditemukan!")
cap.release()
exit(1)
net = cv2.dnn.readNet(WEIGHTS, CFG)
net.setPreferableBackend(cv2.dnn.DNN_BACKEND_OPENCV)
net.setPreferableTarget(cv2.dnn.DNN_TARGET_CPU)
with open(NAMES, "r") as f:
classes = [line.strip() for line in f.readlines()]
layer_names = net.getLayerNames()
output_layers = [layer_names[i - 1] for i in net.getUnconnectedOutLayers()]
print("✅ Model YOLO berhasil dimuat")
# ====================== MQTT SETUP ======================
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code == 0:
print("✅ MQTT terhubung ke broker!")
client.subscribe("classroom/webcam/request")
else:
print(f"❌ MQTT gagal connect: {reason_code}")
def on_message(client, userdata, msg):
if msg.topic == "classroom/webcam/request":
print("📸 Request frame webcam diterima")
send_webcam_frame()
client = mqtt.Client(mqtt_client.CallbackAPIVersion.VERSION2)
client.on_connect = on_connect
client.on_message = on_message
try:
client.connect(MQTT_BROKER, MQTT_PORT, 60)
client.loop_start()
print("✅ MQTT client siap")
except Exception as e:
print(f"❌ Gagal konek MQTT: {e}")
cap.release()
exit(1)
# ====================== FUNGSI CAPTURE ======================
def get_frame():
"""Ambil frame dari webcam"""
if cap is None or not cap.isOpened():
return False, None
return cap.read()
def send_webcam_frame():
"""Mengirim frame webcam via MQTT"""
try:
ret, frame = get_frame()
if ret and frame is not None:
frame = cv2.resize(frame, (WEBCAM_WIDTH, WEBCAM_HEIGHT))
_, buffer = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, WEBCAM_QUALITY])
frame_base64 = base64.b64encode(buffer).decode('utf-8')
client.publish(WEBCAM_TOPIC, frame_base64)
print(f"📸 Frame terkirim ({len(frame_base64) // 1024} KB)")
return True
return False
except Exception as e:
print(f"❌ Error: {e}")
return False
def send_webcam_periodic():
while True:
send_webcam_frame()
time.sleep(WEBCAM_INTERVAL)
# ====================== DETEKSI KEHADIRAN ======================
def detect_person(frame):
"""Deteksi orang dalam frame"""
blob = cv2.dnn.blobFromImage(frame, 0.00392, INPUT_SIZE, (0, 0, 0), True, crop=False)
net.setInput(blob)
outs = net.forward(output_layers)
for out in outs:
for detection in out:
scores = detection[5:]
class_id = np.argmax(scores)
confidence = scores[class_id]
if confidence > CONFIDENCE_THRESHOLD and classes[class_id] == "person":
return True
return False
# ====================== MAIN LOOP ======================
def main():
# Start thread webcam
webcam_thread = threading.Thread(target=send_webcam_periodic, daemon=True)
webcam_thread.start()
print("✅ Thread webcam dimulai")
last_presence = None
frame_count = 0
detection_skip = 2
print("\n" + "=" * 60)
print("🚀 Sistem deteksi kehadiran berjalan...")
print(f" - Webcam streaming: setiap {WEBCAM_INTERVAL} detik")
print(f" - Deteksi kehadiran: setiap {detection_skip} frame")
print(" - Tekan Ctrl+C untuk berhenti")
print("=" * 60 + "\n")
try:
while True:
ret, frame = get_frame()
if not ret or frame is None:
print("⚠️ Gagal membaca frame, mencoba reset...")
time.sleep(1)
continue
frame_count += 1
if frame_count % detection_skip == 0:
person_detected = detect_person(frame)
current_presence = "ada" if person_detected else "tidak ada"
client.publish(PRESENCE_TOPIC, current_presence)
if current_presence != last_presence:
command = "on" if person_detected else "off"
client.publish(CONTROL_TOPIC, command)
status_msg = {
"ac_state": command,
"temperature": 24,
"mode": "cool",
"presence": current_presence,
"timestamp": datetime.now().isoformat()
}
client.publish(STATUS_TOPIC, json.dumps(status_msg))
print(f"👥 {current_presence} → AC {command.upper()}")
last_presence = current_presence
status_indicator = "👥 ADA" if person_detected else "🚪 KOSONG"
print(f"[{datetime.now().strftime('%H:%M:%S')}] {status_indicator} | Frame: {frame_count}")
time.sleep(0.05)
except KeyboardInterrupt:
print("\n\n🛑 Berhenti...")
finally:
if cap:
cap.release()
client.disconnect()
print("✅ Selesai")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,57 @@
---
name: developing-genkit-dart
description: Generates code and provides documentation for the Genkit Dart SDK. Use when the user asks to build AI agents in Dart, use Genkit flows, or integrate LLMs into Dart/Flutter applications.
metadata:
genkit-managed: true
---
# Genkit Dart
Genkit Dart is an AI SDK for Dart that provides a unified interface for code generation, structured outputs, tools, flows, and AI agents.
## Core Features and Usage
If you need help with initializing Genkit (`Genkit()`), Generation (`ai.generate`), Tooling (`ai.defineTool`), Flows (`ai.defineFlow`), Embeddings (`ai.embedMany`), streaming, or calling remote flow endpoints, please load the core framework reference:
[references/genkit.md](references/genkit.md)
## Genkit CLI (recommended)
The Genkit CLI provides a local development UI for running Flow, tracing executions, playing with models, and evaluating outputs.
check if the user has it installed: `genkit --version`
**Installation:**
```bash
curl -sL cli.genkit.dev | bash # Native CLI
# OR
npm install -g genkit-cli # Via npm
```
**Usage:**
Wrap your run command with `genkit start` to attach the Genkit developer UI and tracing:
```bash
genkit start -- dart run main.dart
```
## Plugin Ecosystem
Genkit relies on a large suite of plugins to perform generative AI actions, interface with external LLMs, or host web servers.
When asked to use any given plugin, always verify usage by referring to its corresponding reference below. You should load the reference when you need to know the specific initialization arguments, tools, models, and usage patterns for the plugin:
| Plugin Name | Reference Link | Description |
| ---- | ---- | ---- |
| `genkit_google_genai` | [references/genkit_google_genai.md](references/genkit_google_genai.md) | Load for Google Gemini plugin interface usage. |
| `genkit_anthropic` | [references/genkit_anthropic.md](references/genkit_anthropic.md) | Load for Anthropic plugin interface for Claude models. |
| `genkit_openai` | [references/genkit_openai.md](references/genkit_openai.md) | Load for OpenAI plugin interface for GPT models, Groq, and custom compatible endpoints. |
| `genkit_middleware` | [references/genkit_middleware.md](references/genkit_middleware.md) | Load for Tooling for specific agentic behavior: `filesystem`, `skills`, and `toolApproval` interrupts. |
| `genkit_mcp` | [references/genkit_mcp.md](references/genkit_mcp.md) | Load for Model Context Protocol integration (Server, Host, and Client capabilities). |
| `genkit_chrome` | [references/genkit_chrome.md](references/genkit_chrome.md) | Load for Running Gemini Nano locally inside the Chrome browser using the Prompt API. |
| `genkit_shelf` | [references/genkit_shelf.md](references/genkit_shelf.md) | Load for Integrating Genkit Flow actions over HTTP using Dart Shelf. |
| `genkit_firebase_ai` | [references/genkit_firebase_ai.md](references/genkit_firebase_ai.md) | Load for Firebase AI plugin interface (Gemini API via Vertex AI). |
## External Dependencies
Whenever you define schemas mapping inside of Tools, Flows, and Prompts, you must use the [schemantic](https://pub.dev/packages/schemantic) library.
To learn how to use schemantic, ensure you read [references/schemantic.md](references/schemantic.md) for how to implement type safe generated Dart code. This is particularly relevant when you encounter symbols like `@Schema()`, `SchemanticType`, or classes with the `$` prefix. Genkit Dart uses schemantic for all of its data models so it's a CRITICAL skill to understand for using Genkit Dart.
## Best Practices
- Always check that code cleanly compiles using `dart analyze` before generating the final response.
- Always use the Genkit CLI for local development and debugging.

View File

@ -0,0 +1,380 @@
# Genkit Core Framework
Genkit Dart is an AI SDK for Dart that provides a unified interface for text generation, structured output, tool calling, and agentic workflows.
## Initialization
```dart
import 'package:genkit/genkit.dart';
import 'package:genkit_google_genai/genkit_google_genai.dart'; // Or any other plugin
void main() async {
// Pass plugins to use into the Genkit constructor
final ai = Genkit(plugins: [googleAI()]);
}
```
## Generate Text
```dart
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash'), // Needs a model reference from a plugin
prompt: 'Explain quantum computing in simple terms.',
);
print(response.text);
```
## Stream Responses
```dart
final stream = ai.generateStream(
model: googleAI.gemini('gemini-2.5-flash'),
prompt: 'Write a short story about a robot learning to paint.',
);
await for (final chunk in stream) {
print(chunk.text);
}
```
## Embed Text
```dart
final embeddings = await ai.embedMany(
documents: [
DocumentData(content: [TextPart(text: 'Hello world')]),
],
embedder: googleAI.textEmbedding('text-embedding-004'),
);
print(embeddings.first.embedding);
```
## Define Tools
Models can use define actions and access external data via custom defined tools.
Requires the `schemantic` library for schema definitions.
```dart
import 'package:schemantic/schemantic.dart';
@Schema()
abstract class $WeatherInput {
String get location;
}
final weatherTool = ai.defineTool(
name: 'getWeather',
description: 'Gets the current weather for a location',
inputSchema: WeatherInput.$schema,
fn: (input, _) async {
// Call your weather API here
return 'Weather in ${input.location}: 72°F and sunny';
},
);
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash'),
prompt: 'What\'s the weather like in San Francisco?',
toolNames: ['getWeather'], // Use the tools
);
```
## Structured Output
You can ensure the generative model returns a typed JSON object by providing an `outputSchema`.
```dart
@Schema()
abstract class $Person {
String get name;
int get age;
}
// ... inside main ...
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash'),
prompt: 'Generate a person named John Doe, age 30',
outputSchema: Person.$schema, // Force the model to return this schema
);
final person = response.output; // Typed Person object
print('Name: ${person.name}, Age: ${person.age}');
```
## Define Flows
Wrap your AI logic in flows for better observability, testing, and deployment:
```dart
final jokeFlow = ai.defineFlow(
name: 'tellJoke',
inputSchema: .string(),
outputSchema: .string(),
fn: (topic, _) async {
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash'),
prompt: 'Tell me a joke about $topic',
);
return response.text; // Value return
},
);
final joke = await jokeFlow('programming');
print(joke);
```
### Streaming Flows
Stream data from your flows using `context.sendChunk(...)` and returning the final value:
```dart
final streamStory = ai.defineFlow(
name: 'streamStory',
inputSchema: .string(),
outputSchema: .string(),
streamSchema: .string(),
fn: (topic, context) async {
final stream = ai.generateStream(
model: googleAI.gemini('gemini-2.5-flash'),
prompt: 'Write a story about $topic',
);
await for (final chunk in stream) {
context.sendChunk(chunk.text); // Stream the chunks
}
return 'Story complete'; // Value return
},
);
```
## Calling remote Flows from a dart client
The `genkit` package provides `package:genkit/client.dart` representing remote Genkit actions that can be invoked or streamed using type-safe definitions.
1. Defines a remote action
```dart
import 'package:genkit/client.dart';
final stringAction = defineRemoteAction(
url: 'http://localhost:3400/my-flow',
inputSchema: .string(),
outputSchema: .string(),
);
```
2. Call the Remote Action (Non-streaming)
```dart
final response = await stringAction(input: 'Hello from Dart!');
print('Flow Response: $response');
```
3. Call the Remote Action (Streaming)
Use the `.stream()` method on the action flow, and access `stream.onResult` to wait on the async return value.
```dart
final streamAction = defineRemoteAction(
url: 'http://localhost:3400/stream-story',
inputSchema: .string(),
outputSchema: .string(),
streamSchema: .string(),
);
final stream = streamAction.stream(
input: 'Tell me a short story about a Dart developer.',
);
await for (final chunk in stream) {
print('Chunk: $chunk');
}
final finalResult = await stream.onResult;
print('\nFinal Response: $finalResult');
```
## Calling remote Flows from a Javascript client
Install `genkit` npm package:
```bash
npm install genkit
```
1. Call a remote flow (non-streaming)
```ts
import { runFlow } from 'genkit/beta/client';
async function callHelloFlow() {
try {
const result = await runFlow({
url: 'http://127.0.0.1:3400/helloFlow', // Replace with your deployed flow's URL
input: { name: 'Genkit User' },
});
console.log('Non-streaming result:', result.greeting);
} catch (error) {
console.error('Error calling helloFlow:', error);
}
}
callHelloFlow();
```
2. Call a remote flow (streaming)
```ts
import { streamFlow } from 'genkit/beta/client';
async function streamHelloFlow() {
try {
const result = streamFlow({
url: 'http://127.0.0.1:3400/helloFlow', // Replace with your deployed flow's URL
input: { name: 'Streaming User' },
});
// Process the stream chunks as they arrive
for await (const chunk of result.stream) {
console.log('Stream chunk:', chunk);
}
// Get the final complete response
const finalOutput = await result.output;
console.log('Final streaming output:', finalOutput.greeting);
} catch (error) {
console.error('Error streaming helloFlow:', error);
}
}
streamHelloFlow();
```
## Data Models
Genkit uses standard data models for representing prompts (messages & parts) and responses. These classes are implemented using schemantic library.
```dart
import 'package:genkit/genkit.dart';
import 'package:schemantic/schemantic.dart';
@Schema()
abstract class $MyDataModel {
// uses Genkit's Message schema (not schemantic's Message)
List<$Message> get messages;
List<$Part> get parts;
}
void example() {
// --- Parts ---
// A Text part
final textPart = TextPart(text: 'some text', metadata: {'foo': 'bar'});
// A Media/Image part
final mediaPart = MediaPart(
media: Media(url: 'https://...', contentType: 'image/png'),
metadata: {'foo': 'bar'},
);
// A Tool Request initiated by the model
final toolRequestPart = ToolRequestPart(
toolRequest: ToolRequest(
name: 'get_weather',
ref: 'abc',
input: {'location': 'Paris, France'},
),
metadata: {'foo': 'bar'},
);
// The resulting data from a Tool execution
final toolResponsePart = ToolResponsePart(
toolResponse: ToolResponse(
name: 'get_weather',
ref: 'abc',
output: {'temperature': '20C'},
),
metadata: {'foo': 'bar'},
);
// Model reasoning (e.g. for Claude's "thinking" models)
final reasoningPart = ReasoningPart(
reasoning: 'thinking...',
metadata: {'foo': 'bar'},
);
// A custom fallback part
final customPart = CustomPart(
custom: {'provider': {'specific': 'data'}},
metadata: {'foo': 'bar'},
);
// --- Messages ---
final systemMessage = Message(
role: Role.system,
content: [textPart, mediaPart],
metadata: {'foo': 'bar'},
);
final userMessage = Message(
role: Role.user,
content: [textPart, mediaPart], // Can contain media (multimodal)
);
final modelMessage = Message(
role: Role.model,
// Models can emit text, tool requests, reasoning, or custom parts
content: [textPart, toolRequestPart, reasoningPart, customPart],
);
// --- Ergonomic Data Access (schema_extensions.dart) ---
// The Genkit SDK provides extensions on `Message` and `Part` to easily access fields
// without needing to cast them manually.
// Get concatenated text from all TextParts in a Message
print(modelMessage.text);
// Get the first Media object from a Message
print(modelMessage.media?.url);
// Iterate over tool requests in a Message
for (final toolReq in modelMessage.toolRequests) {
print(toolReq.name);
}
// Inspect individual parts
for (final part in modelMessage.content) {
if (part.isText) print(part.text);
if (part.isMedia) print(part.media?.url);
if (part.isToolRequest) print(part.toolRequest?.name);
if (part.isToolResponse) print(part.toolResponse?.name);
if (part.isReasoning) print(part.reasoning);
if (part.isCustom) print(part.custom);
}
// --- Streaming Chunks ---
// Data emitted by ai.generateStream() calls
final generateResponseChunk = ModelResponseChunk(
content: [textPart],
index: 0, // Index of the message this chunk belongs to
aggregated: false,
);
// Chunks also have text and media accessors
print(generateResponseChunk.text);
// --- Advanced: Schemas ---
// Use Genkit type schemas directly in Schemantic validations
final messageSchema = Message.$schema;
final partSchema = Part.$schema;
final mySchema = SchemanticType.map(
.string(),
.list(Message.$schema), // Requires a list of Messages
);
// --- Generate Response ---
// ai.generate() returns a GenerateResponseHelper which provides ergonomic getters
// over the underlying ModelResponse:
final response = await ai.generate(...);
print(response.text); // Concatenated text
print(response.media?.url); // First media part
print(response.toolRequests); // All tool requests
print(response.interrupts); // Tool requests that triggered an interrupt
print(response.messages); // Full history of the conversation, including the request and response
print(response.output); // Structured typed output (if outputSchema was used)
}
```

View File

@ -0,0 +1,41 @@
# Genkit Anthropic Plugin (`genkit_anthropic`)
The Anthropic plugin for Genkit Dart, used for interacting with the Claude models.
## Usage
Requires `ANTHROPIC_API_KEY` to be passed to the init block.
```dart
import 'dart:io';
import 'package:genkit/genkit.dart';
import 'package:genkit_anthropic/genkit_anthropic.dart';
void main() async {
final ai = Genkit(
plugins: [anthropic(apiKey: Platform.environment['ANTHROPIC_API_KEY']!)],
);
final response = await ai.generate(
model: anthropic.model('claude-sonnet-4-5'),
prompt: 'Tell me a joke about a developer.',
);
print(response.text);
}
```
## Claude Thinking Configurations
Provides specific configurations for utilizing Claude 3.7+ "thinking" model capabilities.
```dart
final response = await ai.generate(
model: anthropic.model('claude-sonnet-4-5'),
prompt: 'Solve this 24 game: 2, 3, 10, 10',
config: AnthropicOptions(thinking: ThinkingConfig(budgetTokens: 2048)),
);
// The thinking content is available in the message parts
print(response.message?.content);
```

View File

@ -0,0 +1,23 @@
# Genkit Chrome AI Plugin (`genkit_chrome`)
Chrome Built-in AI (Gemini Nano) plugin for Genkit Dart, allowing local offline execution within a Chrome application.
## Usage
```dart
import 'package:genkit/genkit.dart';
import 'package:genkit_chrome/genkit_chrome.dart';
void main() async {
final ai = Genkit(plugins: [ChromeAIPlugin()]);
final stream = ai.generateStream(
model: modelRef('chrome/gemini-nano'),
prompt: 'Write a story about a robot.',
);
await for (final chunk in stream) {
print(chunk.text);
}
}
```

View File

@ -0,0 +1,23 @@
# Genkit Firebase AI Plugin (`genkit_firebase_ai`)
The Firebase AI plugin for Genkit Dart, used for interacting with Gemini APIs through Firebase AI Logic.
## Usage
```dart
import 'package:genkit/genkit.dart';
import 'package:genkit_firebase_ai/genkit_firebase_ai.dart';
void main() async {
// Initialize Genkit with the Firebase AI plugin
final ai = Genkit(plugins: [firebaseAI()]);
// Generate text
final response = await ai.generate(
model: firebaseAI.gemini('gemini-2.5-flash'),
prompt: 'Tell me a joke about a developer.',
);
print(response.text);
}
```

View File

@ -0,0 +1,95 @@
# Genkit Google GenAI Plugin (`genkit_google_genai`)
The Google AI plugin provides an interface against the official Google AI Gemini API.
## Usage
```dart
import 'package:genkit/genkit.dart';
import 'package:genkit_google_genai/genkit_google_genai.dart';
void main() async {
// Initialize Genkit with the Google AI plugin
final ai = Genkit(plugins: [googleAI()]);
// Generate text
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash'),
prompt: 'Tell me a joke about a developer.',
);
print(response.text);
}
```
## Embeddings
```dart
final embeddings = await ai.embedMany(
embedder: googleAI.textEmbedding('text-embedding-004'),
documents: [
DocumentData(content: [TextPart(text: 'Hello world')]),
],
);
```
## Image Generation
The plugin also supports image generation models such as `gemini-2.5-flash-image`.
### Example (Nano Banana)
```dart
// Define an image generation flow
ai.defineFlow(
name: 'imageGenerator',
inputSchema: .string(defaultValue: 'A banana riding a bike'),
outputSchema: Media.$schema,
fn: (input, context) async {
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash-image'),
prompt: input,
);
if (response.media == null) {
throw Exception('No media generated');
}
return response.media!;
},
);
```
The media (url field) contain base64 encoded data uri. You can decode it and save it as a file.
## Text-to-Speech (TTS)
You can use text-to-speech models to generate audio from text. The generated `Media` object will contain base64 encoded PCM audio in its data URI.
```dart
// Define a TTS flow
ai.defineFlow(
name: 'textToSpeech',
inputSchema: .string(defaultValue: 'Genkit is an amazing AI framework!'),
outputSchema: Media.$schema,
fn: (prompt, _) async {
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash-preview-tts'),
prompt: prompt,
config: GeminiTtsOptions(
responseModalities: ['AUDIO'],
speechConfig: SpeechConfig(
voiceConfig: VoiceConfig(
prebuiltVoiceConfig: PrebuiltVoiceConfig(voiceName: 'Puck'),
),
),
),
);
if (response.media != null) {
return response.media!;
}
throw Exception('No audio generated');
},
);
```
Google AI also supports multi-speaker TTS by configuring a `MultiSpeakerVoiceConfig` inside `SpeechConfig`.

View File

@ -0,0 +1,115 @@
# Genkit MCP (`genkit_mcp`)
MCP (Model Context Protocol) integration for Genkit Dart.
## MCP Host (Recommended)
Connect to one or more MCP servers and aggregate their capabilities into the Genkit registry automatically.
```dart
import 'package:genkit/genkit.dart';
import 'package:genkit_mcp/genkit_mcp.dart';
void main() async {
final ai = Genkit();
final host = defineMcpHost(
ai,
McpHostOptionsWithCache(
name: 'my-host',
mcpServers: {
'fs': McpServerConfig(
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', '.'],
),
},
),
);
// Tools can be discovered and executed dynamically using a wildcard...
final response = await ai.generate(
model: 'gemini-2.5-flash',
prompt: 'Summarize the contents of README.md',
toolNames: ['my-host:tool/fs/*'],
);
// ...or by specifying the exact tool name
final exactResponse = await ai.generate(
model: 'gemini-2.5-flash',
prompt: 'Read README.md',
toolNames: ['my-host:tool/fs/read_file'],
);
}
```
## MCP Client (Advanced / Single Server)
Connecting to a single MCP server with a client object is an advanced usecase for when you need manual control over the client lifecycle. Standalone clients do not automatically register tools into the registry, so they must be passed into `generate` or `defineDynamicActionProvider` manually.
```dart
import 'package:genkit/genkit.dart';
import 'package:genkit_mcp/genkit_mcp.dart';
void main() async {
final ai = Genkit();
final client = createMcpClient(
McpClientOptions(
name: 'my-client',
mcpServer: McpServerConfig(
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', '.'],
),
),
);
await client.ready();
// Retrieve the tools from the connected client
final tools = await client.getActiveTools(ai);
final response = await ai.generate(
model: 'gemini-2.5-flash',
prompt: 'Read the contents of README.md',
tools: tools,
);
}
```
## MCP Server
Expose Genkit actions (tools, prompts, resources) over MCP.
```dart
import 'package:genkit/genkit.dart';
import 'package:genkit_mcp/genkit_mcp.dart';
void main() async {
final ai = Genkit();
ai.defineTool(
name: 'add',
description: 'Add two numbers together',
inputSchema: .map(.string(), .dynamicSChema()),
fn: (input, _) async => (input['a'] + input['b']).toString(),
);
ai.defineResource(
name: 'my-resource',
uri: 'my://resource',
fn: (_, _) async => ResourceOutput(content: [TextPart(text: 'my resource')]),
);
// Stdio transport by default
final server = createMcpServer(ai, McpServerOptions(name: 'my-server'));
await server.start();
}
```
### Streamable HTTP Transport
```dart
import 'dart:io';
final transport = await StreamableHttpServerTransport.bind(
address: InternetAddress.loopbackIPv4,
port: 3000,
);
await server.start(transport);
```

View File

@ -0,0 +1,84 @@
# Genkit Middleware (`genkit_middleware`)
A collection of useful middleware for Genkit Dart to enhance your agent's capabilities. Register plugins when initializing Genkit:
```dart
import 'package:genkit/genkit.dart';
import 'package:genkit_middleware/genkit_middleware.dart';
void main() {
final ai = Genkit(
plugins: [
FilesystemPlugin(),
SkillsPlugin(),
ToolApprovalPlugin(),
],
);
}
```
## Filesystem Middleware
Allows the agent to list, read, write, and search/replace files within a restricted root directory.
```dart
final response = await ai.generate(
prompt: 'Check the logs in the current directory.',
use: [
filesystem(rootDirectory: '/path/to/secure/workspace'),
],
);
```
**Tools Provided:**
- `list_files`, `read_file`, `write_file`, `search_and_replace`
## Skills Middleware
Injects specialized instructions (skills) into the system prompt from `SKILL.md` files located in specified directories.
```dart
final response = await ai.generate(
prompt: 'Help me debug this issue.',
use: [
skills(skillPaths: ['/path/to/skills']),
],
);
```
**Tools Provided:**
- `use_skill`: Retrieve the full content of a skill by name.
## Tool Approval Middleware
Intercepts tool execution for specified tools and requires explicit approval. Returns `FinishReason.interrupted`.
```dart
final response = await ai.generate(
prompt: 'Delete the database.',
use: [
// Require approval for all tools EXCEPT those below
toolApproval(approved: ['read_file', 'list_files']),
],
);
if (response.finishReason == FinishReason.interrupted) {
final interrupt = response.interrupts.first;
// Ask user for approval
final isApproved = await askUser();
if (isApproved) {
final resumeResponse = await ai.generate(
messages: response.messages, // Pass history
toolChoice: ToolChoice.none, // Prevent immediate re-call
interruptRestart: [
ToolRequestPart(
toolRequest: interrupt.toolRequest,
metadata: {
...?interrupt.metadata,
'tool-approved': true
},
),
],
);
}
}
```

View File

@ -0,0 +1,54 @@
# Genkit OpenAI Plugin (`genkit_openai`)
OpenAI-compatible API plugin for Genkit Dart. Supports OpenAI models and other compatible APIs (xAI, DeepSeek, Together AI, Groq, etc.).
## Basic Usage
```dart
import 'dart:io';
import 'package:genkit/genkit.dart';
import 'package:genkit_openai/genkit_openai.dart';
void main() async {
final ai = Genkit(plugins: [
openAI(apiKey: Platform.environment['OPENAI_API_KEY']),
]);
final response = await ai.generate(
model: openAI.model('gpt-4o'),
prompt: 'Tell me a joke.',
);
}
```
## Options
`OpenAIOptions` allows configuring sampling temperature, nucleus sampling, token generation, seed, etc:
`config: OpenAIOptions(temperature: 0.7, maxTokens: 100)`
## Groq API override
Specify custom `baseUrl` and custom models to integrate with third-party providers.
```dart
final ai = Genkit(plugins: [
openAI(
apiKey: Platform.environment['GROQ_API_KEY'],
baseUrl: 'https://api.groq.com/openai/v1',
models: [
CustomModelDefinition(
name: 'llama-3.3-70b-versatile',
info: ModelInfo(
label: 'Llama 3.3 70B',
supports: {'multiturn': true, 'tools': true, 'systemRole': true},
),
),
],
),
]);
final response = await ai.generate(
model: openAI.model('llama-3.3-70b-versatile'),
prompt: 'Hello!',
);
```

View File

@ -0,0 +1,59 @@
# Genkit Shelf Plugin (`genkit_shelf`)
Shelf integration for Genkit Dart, used to serve Genkit Flows.
## Standalone Server
Serve Genkit Flows easily on an isolated HTTP server using `startFlowServer`.
```dart
import 'package:genkit/genkit.dart';
import 'package:genkit_shelf/genkit_shelf.dart';
void main() async {
final ai = Genkit();
final flow = ai.defineFlow(
name: 'myFlow',
inputSchema: .string(),
outputSchema: .string(),
fn: (String input, _) async => 'Hello $input',
);
await startFlowServer(
flows: [flow],
port: 8080,
);
}
```
## Existing Shelf Application
Mount Genkit Flow endpoints directly to an existing Shelf `Router` using `shelfHandler`.
```dart
import 'package:genkit/genkit.dart';
import 'package:genkit_shelf/genkit_shelf.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_router/shelf_router.dart';
void main() async {
final ai = Genkit();
final flow = ai.defineFlow(
name: 'myFlow',
inputSchema: .string(),
outputSchema: .string(),
fn: (String input, _) async => 'Hello $input',
);
final router = Router();
// Mount the flow handler at a specific path
router.post('/myFlow', shelfHandler(flow));
// Start the server
await io.serve(router.call, 'localhost', 8080);
}
```
Access deployed flows using genkit client libraries (from Dart or JS).

View File

@ -0,0 +1,137 @@
# Schemantic
Schemantic is a general-purpose Dart library used for defining strongly typed data classes that automatically bind to reusable runtime JSON schemas. It is standard for the `genkit-dart` framework but works independently as well.
## Core Concepts
Always use `schemantic` when strongly typed JSON parsing or programmatic schema validation is required.
- Annotate your abstract classes with `@Schema()`.
- Use the `$` prefix for abstract schema class names (e.g., `abstract class $User`).
- Always run `dart run build_runner build` to generate the `.g.dart` schema files.
## Installation
Add dependencies:
```bash
dart pub add schemantic
```
## Basic Usage
1. **Defining a schema:**
```dart
import 'package:schemantic/schemantic.dart';
part 'my_file.g.dart'; // Must match the filename
@Schema()
abstract class $MyObj {
String get name;
$MySubObj get subObj;
}
@Schema()
abstract class $MySubObj {
String get foo;
}
```
2. **Using the Generated Class:**
The builder creates a concrete class `MyObj` (no `$`) with a factory constructor (`MyObj.fromJson`) and a regular constructor.
```dart
// Creating an instance
final obj = MyObj(name: 'test', subObj: MySubObj(foo: 'bar'));
// Serializing to JSON
print(obj.toJson());
// Parsing from JSON
final parsed = MyObj.fromJson({'name': 'test', 'subObj': {'foo': 'bar'}});
```
3. **Accessing Schemas at Runtime:**
The generated data classes have a static `$schema` field (of type `SchemanticType<T>`) which can be used to pass the definition into functions or to extract the raw JSON schema.
```dart
// Access JSON schema
final schema = MyObj.$schema.jsonSchema;
print(schema.toJson());
// Validate arbitrary JSON at runtime
final validationErrors = await schema.validate({'invalid': 'data'});
```
## Primitive Schemas
When a full data class is not required, Schemantic provides functions to create schemas dynamically.
```dart
final ageSchema = SchemanticType.integer(description: 'Age in years', minimum: 0);
final nameSchema = SchemanticType.string(minLength: 2);
final nothingSchema = SchemanticType.voidSchema();
final anySchema = SchemanticType.dynamicSchema();
final userSchema = SchemanticType.map(.string(), .integer()); // Map<String, int>
final tagsSchema = SchemanticType.list(.string()); // List<String>
```
## Union Types (AnyOf)
To allow a field to accept multiple types, use `@AnyOf`.
```dart
@Schema()
abstract class $Poly {
@AnyOf([int, String, $MyObj])
Object? get id;
}
```
Schemantic generates a specific helper class (e.g., `PolyId`) to handle the values:
```dart
final poly1 = Poly(id: PolyId.int(123));
final poly2 = Poly(id: PolyId.string('abc'));
```
## Field Annotations
You can use specialized annotations for more validation boundaries:
```dart
@Schema()
abstract class $User {
@IntegerField(
name: 'years_old', // Change JSON key
description: 'Age of the user',
minimum: 0,
defaultValue: 18,
)
int? get age;
@StringField(
minLength: 2,
enumValues: ['user', 'admin'],
)
String get role;
}
```
## Recursive Schemas
For recursive structures (like trees), must use `useRefs: true` inside the generated jsonSchema property. You define it normally:
```dart
@Schema()
abstract class $Node {
String get id;
List<$Node>? get children;
}
```
*Note*: `Node.$schema.jsonSchema(useRefs: true)` generates schemas with JSON Schema `$ref`.

View File

@ -0,0 +1,97 @@
---
name: developing-genkit-go
description: Develop AI-powered applications using Genkit in Go. Use when the user asks to build AI features, agents, flows, or tools in Go using Genkit, or when working with Genkit Go code involving generation, prompts, streaming, tool calling, or model providers.
metadata:
genkit-managed: true
---
# Genkit Go
Genkit Go is an AI SDK for Go that provides generation, structured output, streaming, tool calling, prompts, and flows with a unified interface across model providers.
## Hello World
```go
package main
import (
"context"
"fmt"
"log"
"net/http"
"github.com/genkit-ai/genkit/go/ai"
"github.com/genkit-ai/genkit/go/genkit"
"github.com/genkit-ai/genkit/go/plugins/googlegenai"
"github.com/genkit-ai/genkit/go/plugins/server"
)
func main() {
ctx := context.Background()
g := genkit.Init(ctx, genkit.WithPlugins(&googlegenai.GoogleAI{}))
genkit.DefineFlow(g, "jokeFlow", func(ctx context.Context, topic string) (string, error) {
return genkit.GenerateText(ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithPrompt("Tell me a joke about %s", topic),
)
})
mux := http.NewServeMux()
for _, f := range genkit.ListFlows(g) {
mux.HandleFunc("POST /"+f.Name(), genkit.Handler(f))
}
log.Fatal(server.Start(ctx, "127.0.0.1:8080", mux))
}
```
## Core Features
Load the appropriate reference based on what you need:
| Feature | Reference | When to load |
| --- | --- | --- |
| Initialization | [references/getting-started.md](references/getting-started.md) | Setting up `genkit.Init`, plugins, the `*Genkit` instance pattern |
| Generation | [references/generation.md](references/generation.md) | `Generate`, `GenerateText`, `GenerateData`, streaming, output formats |
| Prompts | [references/prompts.md](references/prompts.md) | `DefinePrompt`, `DefineDataPrompt`, `.prompt` files, schemas |
| Tools | [references/tools.md](references/tools.md) | `DefineTool`, tool interrupts, `RestartWith`/`RespondWith` |
| Flows & HTTP | [references/flows-and-http.md](references/flows-and-http.md) | `DefineFlow`, `DefineStreamingFlow`, `genkit.Handler`, HTTP serving |
| Model Providers | [references/providers.md](references/providers.md) | Google AI, Vertex AI, Anthropic, OpenAI-compatible, Ollama setup |
## Genkit CLI
Check if installed: `genkit --version`
**Installation:**
```bash
curl -sL cli.genkit.dev | bash
```
**Key commands:**
```bash
# Start app with Developer UI (tracing, flow testing) at http://localhost:4000
genkit start -- go run .
genkit start -o -- go run . # also opens browser
# Run a flow directly from the CLI
genkit flow:run myFlow '{"data": "input"}'
genkit flow:run myFlow '{"data": "input"}' --stream # with streaming
genkit flow:run myFlow '{"data": "input"}' --wait # wait for completion
# Look up Genkit documentation
genkit docs:search "streaming" go
genkit docs:list go
genkit docs:read go/flows.md
```
See [references/getting-started.md](references/getting-started.md) for full CLI and Developer UI details.
## Key Guidance
- **Pass `g` explicitly.** The `*Genkit` instance returned by `genkit.Init` is the central registry. Pass it to all Genkit functions rather than storing it as a global. This is a core pattern throughout the SDK.
- **Wrap AI logic in flows.** Flows give you tracing, observability, HTTP deployment via `genkit.Handler`, and the ability to test from the Developer UI and CLI. Any generation call worth keeping should live in a flow.
- **Use `jsonschema:"description=..."` struct tags on output types.** The model uses these descriptions to understand what each field should contain. Without them, structured output quality drops significantly.
- **Write good tool descriptions.** The model decides which tools to call based on their description string. Vague descriptions lead to missed or incorrect tool calls.
- **Use `.prompt` files for complex prompts.** They separate prompt content from Go code, support Handlebars templating, and can be iterated on without recompilation. Code-defined prompts are better for simple, single-line cases.
- **Look up the latest model IDs.** Model names change frequently. Check provider documentation for current model IDs rather than relying on hardcoded names. See [references/providers.md](references/providers.md).

View File

@ -0,0 +1,183 @@
# Flows & HTTP
## DefineFlow
Wrap AI logic in a flow for observability, tracing, and HTTP deployment.
```go
jokeFlow := genkit.DefineFlow(g, "jokeFlow",
func(ctx context.Context, topic string) (string, error) {
return genkit.GenerateText(ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithPrompt("Tell me a joke about %s", topic),
)
},
)
```
### Running a Flow Directly
```go
result, err := jokeFlow.Run(ctx, "cats")
```
## DefineStreamingFlow
Flows that stream chunks back to the caller. Two common patterns:
### Pattern 1: Passthrough Streaming
Pass the stream callback directly through to `WithStreaming`. The callback type is `ai.ModelStreamCallback` = `func(context.Context, *ai.ModelResponseChunk) error`:
```go
genkit.DefineStreamingFlow(g, "streamingJokeFlow",
func(ctx context.Context, topic string, sendChunk ai.ModelStreamCallback) (string, error) {
resp, err := genkit.Generate(ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithPrompt("Tell me a long joke about %s", topic),
ai.WithStreaming(sendChunk), // passthrough
)
if err != nil {
return "", err
}
return resp.Text(), nil
},
)
```
### Pattern 2: Manual String Streaming
Use `core.StreamCallback[string]` to stream extracted text:
```go
genkit.DefineStreamingFlow(g, "streamingJokeFlow",
func(ctx context.Context, topic string, sendChunk core.StreamCallback[string]) (string, error) {
stream := genkit.GenerateStream(ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithPrompt("Tell me a long joke about %s", topic),
)
for result, err := range stream {
if err != nil {
return "", err
}
if result.Done {
return result.Response.Text(), nil
}
sendChunk(ctx, result.Chunk.Text())
}
return "", nil
},
)
```
### Typed Streaming Flows
Use `core.StreamCallback[T]` with `GenerateDataStream` for typed chunks:
```go
genkit.DefineStreamingFlow(g, "structuredStream",
func(ctx context.Context, input JokeRequest, sendChunk core.StreamCallback[*Joke]) (*Joke, error) {
stream := genkit.GenerateDataStream[*Joke](ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithPrompt("Tell me a joke about %s", input.Topic),
)
for result, err := range stream {
if err != nil { return nil, err }
if result.Done { return result.Output, nil }
sendChunk(ctx, result.Chunk)
}
return nil, nil
},
)
```
## Named Sub-Steps
Use `core.Run` inside a flow for traced sub-steps:
```go
genkit.DefineFlow(g, "pipeline",
func(ctx context.Context, input string) (string, error) {
subject, err := core.Run(ctx, "extract-subject", func() (string, error) {
return genkit.GenerateText(ctx, g,
ai.WithPrompt("Extract the subject from: %s", input),
)
})
if err != nil { return "", err }
joke, err := core.Run(ctx, "generate-joke", func() (string, error) {
return genkit.GenerateText(ctx, g,
ai.WithPrompt("Tell me a joke about %s", subject),
)
})
return joke, err
},
)
```
## HTTP Handlers
### genkit.Handler
Convert any flow into an `http.HandlerFunc`:
```go
mux := http.NewServeMux()
for _, f := range genkit.ListFlows(g) {
mux.HandleFunc("POST /"+f.Name(), genkit.Handler(f))
}
log.Fatal(server.Start(ctx, "127.0.0.1:8080", mux))
```
### Request/Response Format
**Non-streaming request:**
```bash
curl -X POST http://localhost:8080/jokeFlow \
-H "Content-Type: application/json" \
-d '{"data": "bananas"}'
```
Response: `{"result": "Why did the banana go to the doctor?..."}`
**Streaming request:**
```bash
curl -N -X POST http://localhost:8080/streamingJokeFlow \
-H "Content-Type: application/json" \
-d '{"data": "bananas"}'
```
Streaming responses use Server-Sent Events (SSE) format.
### genkit.HandlerFunc
For frameworks that expect error-returning handlers:
```go
handler := genkit.HandlerFunc(myFlow)
// handler is func(http.ResponseWriter, *http.Request) error
```
### Context Providers
Inject request context (e.g., auth headers) into flow execution:
```go
mux.HandleFunc("POST /myFlow", genkit.Handler(myFlow,
genkit.WithContextProviders(func(ctx context.Context, rd core.RequestData) (api.ActionContext, error) {
// rd.Headers contains HTTP headers
return api.ActionContext{"userId": rd.Headers.Get("X-User-Id")}, nil
}),
))
```
### ListFlows
Get all registered flows for dynamic route setup:
```go
flows := genkit.ListFlows(g) // []api.Action
for _, f := range flows {
fmt.Println(f.Name())
}
```

View File

@ -0,0 +1,176 @@
# Generation
## GenerateText
Simplest form. Returns a string.
```go
text, err := genkit.GenerateText(ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithPrompt("Tell me a joke about %s", topic),
)
```
## Generate
Returns a full `*ModelResponse` with metadata, usage stats, and history.
```go
resp, err := genkit.Generate(ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithSystem("You are a helpful assistant."),
ai.WithPrompt("Explain %s", topic),
)
fmt.Println(resp.Text()) // concatenated text
fmt.Println(resp.FinishReason) // ai.FinishReasonStop, etc.
fmt.Println(resp.Usage) // token counts
```
## GenerateData (Structured Output)
Returns a typed Go value parsed from the model's JSON output.
```go
type Joke struct {
Setup string `json:"setup" jsonschema:"description=The setup of the joke"`
Punchline string `json:"punchline" jsonschema:"description=The punchline"`
}
joke, resp, err := genkit.GenerateData[Joke](ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithPrompt("Tell me a joke about %s", topic),
)
// joke is *Joke, resp is *ModelResponse
```
## Streaming
### GenerateStream
Returns an iterator. Each value has `.Done`, `.Chunk`, and `.Response`.
```go
stream := genkit.GenerateStream(ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithPrompt("Tell me a long story about %s", topic),
)
for result, err := range stream {
if err != nil {
return err
}
if result.Done {
finalText := result.Response.Text()
break
}
fmt.Print(result.Chunk.Text()) // incremental text
}
```
### GenerateDataStream (Structured Streaming)
Streams typed partial objects as they arrive.
```go
stream := genkit.GenerateDataStream[Joke](ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithPrompt("Tell me a joke about %s", topic),
)
for result, err := range stream {
if err != nil {
return err
}
if result.Done {
finalJoke := result.Output // *Joke
break
}
partialJoke := result.Chunk // *Joke (partial)
}
```
### Callback-Based Streaming
Use `ai.WithStreaming` with `Generate` for callback-style streaming. The callback receives `*ai.ModelResponseChunk`:
```go
resp, err := genkit.Generate(ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithPrompt("Tell me a story"),
ai.WithStreaming(func(ctx context.Context, chunk *ai.ModelResponseChunk) error {
fmt.Print(chunk.Text()) // extract text from chunk
return nil
}),
)
// resp contains the final complete response
```
## Common Options
```go
// Model selection
ai.WithModel(googlegenai.ModelRef("googleai/gemini-flash-latest", nil)) // model reference
ai.WithModelName("googleai/gemini-flash-latest") // by name string
// Content
ai.WithPrompt("Tell me about %s", topic) // user message (supports fmt verbs)
ai.WithSystem("You are a pirate.") // system instructions
ai.WithMessages(msg1, msg2) // conversation history
ai.WithDocs(doc1, doc2) // context documents
ai.WithTextDocs("context 1", "context 2") // context as strings
// Model config (provider-specific)
ai.WithConfig(map[string]any{"temperature": 0.7})
```
## Output Formats
Control how the model structures its output.
### By Go Type
```go
// Automatically uses JSON format and instructs model to match the type
ai.WithOutputType(MyStruct{})
```
### By Format String
```go
ai.WithOutputFormat(ai.OutputFormatJSON) // single JSON object
ai.WithOutputFormat(ai.OutputFormatJSONL) // JSON Lines (one object per line)
ai.WithOutputFormat(ai.OutputFormatArray) // JSON array
ai.WithOutputFormat(ai.OutputFormatEnum) // constrained enum value
ai.WithOutputFormat(ai.OutputFormatText) // plain text (default)
```
### Enum Output
```go
type Color string
const (
Red Color = "red"
Green Color = "green"
Blue Color = "blue"
)
text, err := genkit.GenerateText(ctx, g,
ai.WithPrompt("What color is the sky?"),
ai.WithOutputEnums(Red, Green, Blue),
)
```
### Custom Output Instructions
```go
ai.WithOutputInstructions("Return a JSON object with fields: name (string), age (number)")
```
### Combining Format + Schema
```go
// JSONL with a typed schema (useful for streaming lists)
genkit.DefinePrompt(g, "characters",
ai.WithPrompt("Generate 5 story characters"),
ai.WithOutputType([]StoryCharacter{}),
ai.WithOutputFormat(ai.OutputFormatJSONL),
)
```

View File

@ -0,0 +1,142 @@
# Getting Started
## Project Setup
```bash
mkdir my-genkit-app && cd my-genkit-app
go mod init my-genkit-app
go get github.com/genkit-ai/genkit/go@latest
```
Add provider plugin(s) for the models you want to use:
```bash
go get github.com/genkit-ai/genkit/go/plugins/googlegenai # Google AI / Vertex AI
go get github.com/genkit-ai/genkit/go/plugins/anthropic # Anthropic Claude
go get github.com/genkit-ai/genkit/go/plugins/compat_oai # OpenAI-compatible
go get github.com/genkit-ai/genkit/go/plugins/ollama # Ollama (local)
```
After writing your code, run `go mod tidy` to resolve all dependencies.
## Initialization
Every Genkit app starts with `genkit.Init`, which returns a `*Genkit` instance:
```go
import (
"context"
"github.com/genkit-ai/genkit/go/genkit"
"github.com/genkit-ai/genkit/go/plugins/googlegenai"
)
ctx := context.Background()
g := genkit.Init(ctx,
genkit.WithPlugins(&googlegenai.GoogleAI{}),
)
```
### The `*Genkit` Instance
The `*Genkit` value `g` is the central registry. Pass it to every Genkit function:
```go
// Defining resources
genkit.DefineFlow(g, "myFlow", ...)
genkit.DefineTool(g, "myTool", ...)
genkit.DefinePrompt(g, "myPrompt", ...)
// Generating content
genkit.GenerateText(ctx, g, ...)
genkit.Generate(ctx, g, ...)
```
Do not store `g` in a global variable. Pass it explicitly through your call chain.
### Init Options
```go
g := genkit.Init(ctx,
// Register one or more plugins
genkit.WithPlugins(&googlegenai.GoogleAI{}, &anthropic.Anthropic{}),
// Set a default model (used when no model is specified)
genkit.WithDefaultModel("googleai/gemini-flash-latest"),
// Set directory for .prompt files (default: "prompts")
genkit.WithPromptDir("my-prompts"),
// Or embed prompts using Go's embed package
// genkit.WithPromptFS(promptsFS),
)
```
### Embedding Prompts
Use `go:embed` to bundle `.prompt` files into the binary:
```go
//go:embed prompts
var promptsFS embed.FS
g := genkit.Init(ctx,
genkit.WithPlugins(&googlegenai.GoogleAI{}),
genkit.WithPromptFS(promptsFS),
)
```
## Genkit CLI
The Genkit CLI provides a local Developer UI for running flows, tracing executions, and inspecting model interactions.
**Install:**
```bash
curl -sL cli.genkit.dev | bash
```
**Verify:**
```bash
genkit --version
```
### Developer UI
Start your app with the Developer UI attached:
```bash
genkit start -- go run .
```
This launches:
- Your app (with tracing enabled)
- The Developer UI at `http://localhost:4000`
- A telemetry API at `http://localhost:4033`
Add `-o` to auto-open the UI in your browser:
```bash
genkit start -o -- go run .
```
The Developer UI lets you:
- Run and test flows interactively
- View traces for each generation call (inputs, outputs, latency, token usage)
- Inspect prompt rendering and tool calls
- Debug multi-step flows with per-step trace data
### Without the CLI
Set `GENKIT_ENV=dev` to enable the reflection API without the CLI:
```bash
GENKIT_ENV=dev go run .
```
## Import Paths
```go
import (
"github.com/genkit-ai/genkit/go/genkit" // Core: Init, Generate*, DefineFlow, etc.
"github.com/genkit-ai/genkit/go/ai" // Types: WithModel, WithPrompt, Message, Part, etc.
"github.com/genkit-ai/genkit/go/core" // Low-level: Run (sub-steps), Flow types
"github.com/genkit-ai/genkit/go/plugins/server" // server.Start for HTTP
)
```

View File

@ -0,0 +1,256 @@
# Prompts
## DefinePrompt
Define a reusable prompt in code with a default model and template.
```go
jokePrompt := genkit.DefinePrompt(g, "joke",
ai.WithModel(googlegenai.ModelRef("googleai/gemini-flash-latest", nil)),
ai.WithInputType(JokeRequest{Topic: "example"}),
ai.WithPrompt("Tell me a joke about {{topic}}."),
)
```
### Execute
```go
resp, err := jokePrompt.Execute(ctx,
ai.WithInput(map[string]any{"topic": "cats"}),
)
fmt.Println(resp.Text())
```
### ExecuteStream
```go
stream := jokePrompt.ExecuteStream(ctx,
ai.WithInput(map[string]any{"topic": "cats"}),
)
for result, err := range stream {
if err != nil { return err }
if result.Done { break }
fmt.Print(result.Chunk.Text())
}
```
### Override Options at Execution
```go
resp, err := jokePrompt.Execute(ctx,
ai.WithInput(map[string]any{"topic": "cats"}),
ai.WithModelName("googleai/gemini-pro-latest"), // override model
ai.WithConfig(map[string]any{"temperature": 0.9}),
ai.WithTools(myTool),
)
```
## DefineDataPrompt (Typed Input/Output)
Strongly-typed prompts with Go generics.
```go
type JokeRequest struct {
Topic string `json:"topic"`
}
type Joke struct {
Setup string `json:"setup" jsonschema:"description=The setup"`
Punchline string `json:"punchline" jsonschema:"description=The punchline"`
}
jokePrompt := genkit.DefineDataPrompt[JokeRequest, *Joke](g, "structured-joke",
ai.WithModel(googlegenai.ModelRef("googleai/gemini-flash-latest", nil)),
ai.WithPrompt("Tell me a joke about {{topic}}."),
)
```
### Execute (typed)
```go
joke, resp, err := jokePrompt.Execute(ctx, JokeRequest{Topic: "cats"})
// joke is *Joke, resp is *ModelResponse
```
### ExecuteStream (typed)
```go
stream := jokePrompt.ExecuteStream(ctx, JokeRequest{Topic: "cats"})
for result, err := range stream {
if err != nil { return err }
if result.Done {
finalJoke := result.Output // *Joke
break
}
fmt.Print(result.Chunk) // partial *Joke
}
```
## .prompt Files (Dotprompt)
Define prompts in separate files with YAML frontmatter and Handlebars templates.
### Basic .prompt File
`prompts/joke.prompt`:
```
---
model: googleai/gemini-flash-latest
input:
schema:
topic: string
---
Tell me a joke about {{topic}}.
```
### Load and Use
```go
// LookupPrompt returns Prompt (untyped: map[string]any input, string output)
jokePrompt := genkit.LookupPrompt(g, "joke")
resp, err := jokePrompt.Execute(ctx,
ai.WithInput(map[string]any{"topic": "cats"}),
)
```
### Typed .prompt File
`prompts/structured-joke.prompt`:
```
---
model: googleai/gemini-flash-latest
config:
thinkingConfig:
thinkingBudget: 0
input:
schema: JokeRequest
output:
format: json
schema: Joke
---
Tell me a joke about {{topic}}.
```
Register Go types so the .prompt file can reference them by name:
```go
genkit.DefineSchemaFor[JokeRequest](g)
genkit.DefineSchemaFor[Joke](g)
jokePrompt := genkit.LookupDataPrompt[JokeRequest, *Joke](g, "structured-joke")
joke, resp, err := jokePrompt.Execute(ctx, JokeRequest{Topic: "cats"})
```
### LoadPrompt (Explicit Path)
```go
prompt := genkit.LoadPrompt(g, "./prompts/countries.prompt", "countries")
resp, err := prompt.Execute(ctx)
```
### .prompt File Features
**Multi-message prompts with roles:**
```
---
model: googleai/gemini-flash-latest
input:
schema:
question: string
---
{{ role "system" }}
You are a helpful assistant.
{{ role "user" }}
{{question}}
```
**Media in prompts:**
```
---
model: googleai/gemini-flash-latest
input:
schema:
videoUrl: string
contentType: string
---
{{ role "user" }}
Summarize this video:
{{media url=videoUrl contentType=contentType}}
```
**Conditionals and loops:**
```
---
input:
schema:
topic: string
dietaryRestrictions?(array): string
---
Write a recipe about {{topic}}.
{{#if dietaryRestrictions}}
Dietary restrictions: {{#each dietaryRestrictions}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}.
{{/if}}
```
**Inline schema in .prompt file:**
```
---
model: googleai/gemini-flash-latest
input:
schema:
topic: string
style?: string
output:
format: json
schema:
title: string
body: string
tags(array): string
---
Write an article about {{topic}}.
{{#if style}}Write in a {{style}} style.{{/if}}
```
## Schemas
### DefineSchemaFor (from Go type)
Registers a Go struct as a named schema for use in `.prompt` files.
```go
genkit.DefineSchemaFor[JokeRequest](g)
genkit.DefineSchemaFor[Joke](g)
```
The schema name matches the Go type name. Use `jsonschema` struct tags for metadata:
```go
type Recipe struct {
Title string `json:"title" jsonschema:"description=The recipe title"`
Difficulty string `json:"difficulty" jsonschema:"enum=easy,enum=medium,enum=hard"`
Ingredients []Ingredient `json:"ingredients"`
Steps []string `json:"steps"`
}
type Ingredient struct {
Name string `json:"name"`
Amount float64 `json:"amount"`
Unit string `json:"unit"`
}
```
### DefineSchema (manual JSON Schema)
```go
genkit.DefineSchema(g, "Recipe", map[string]any{
"type": "object",
"properties": map[string]any{
"title": map[string]any{"type": "string"},
"ingredients": map[string]any{
"type": "array",
"items": map[string]any{"type": "object"},
},
},
"required": []string{"title", "ingredients"},
})
```

View File

@ -0,0 +1,157 @@
# Model Providers
## Google AI (Gemini)
```go
import "github.com/genkit-ai/genkit/go/plugins/googlegenai"
g := genkit.Init(ctx, genkit.WithPlugins(&googlegenai.GoogleAI{}))
```
**Env var:** `GEMINI_API_KEY` or `GOOGLE_API_KEY`
Model names follow the format `googleai/<model-id>`. Look up the latest model IDs at https://ai.google.dev/gemini-api/docs/models.
```go
// By name string
ai.WithModelName("googleai/gemini-flash-latest")
// Model ref with provider-specific config
ai.WithModel(googlegenai.ModelRef("googleai/gemini-flash-latest", &genai.GenerateContentConfig{
ThinkingConfig: &genai.ThinkingConfig{
ThinkingBudget: genai.Ptr[int32](0), // disable thinking
},
}))
// Lookup a model instance
m := googlegenai.GoogleAIModel(g, "gemini-flash-latest")
```
## Vertex AI
```go
import "github.com/genkit-ai/genkit/go/plugins/googlegenai"
g := genkit.Init(ctx, genkit.WithPlugins(&googlegenai.VertexAI{}))
```
**Env vars:** `GOOGLE_CLOUD_PROJECT`, `GOOGLE_CLOUD_LOCATION` (or `GOOGLE_CLOUD_REGION`)
Uses Application Default Credentials (`gcloud auth application-default login`).
Model names follow the format `vertexai/<model-id>`. Same model IDs as Google AI.
```go
ai.WithModelName("vertexai/gemini-flash-latest")
```
## Anthropic (Claude)
```go
import (
"github.com/anthropics/anthropic-sdk-go" // Anthropic SDK types
ant "github.com/genkit-ai/genkit/go/plugins/anthropic" // Genkit plugin
)
g := genkit.Init(ctx, genkit.WithPlugins(&ant.Anthropic{}))
```
**Env var:** `ANTHROPIC_API_KEY`
Model names follow the format `anthropic/<model-id>`. Look up the latest model IDs at https://docs.anthropic.com/en/docs/about-claude/models.
```go
// By name
ai.WithModelName("anthropic/claude-sonnet-4-6")
// With provider-specific config (uses Anthropic SDK types via ai.WithConfig)
ai.WithConfig(&anthropic.MessageNewParams{
Temperature: anthropic.Float(1.0),
MaxTokens: *anthropic.IntPtr(2000),
Thinking: anthropic.ThinkingConfigParamUnion{
OfEnabled: &anthropic.ThinkingConfigEnabledParam{
BudgetTokens: *anthropic.IntPtr(1024),
},
},
})
```
## OpenAI-Compatible (compat_oai)
Works with any OpenAI-compatible API: OpenAI, DeepSeek, xAI, etc.
```go
import "github.com/genkit-ai/genkit/go/plugins/compat_oai"
openaiPlugin := &compat_oai.OpenAICompatible{
Provider: "openai", // unique identifier
APIKey: os.Getenv("OPENAI_API_KEY"),
// BaseURL: "https://custom-endpoint/v1", // for non-OpenAI providers
}
g := genkit.Init(ctx, genkit.WithPlugins(openaiPlugin))
```
Define models explicitly (not auto-discovered):
```go
model := openaiPlugin.DefineModel("openai", "gpt-4o", compat_oai.ModelOptions{})
```
Use with:
```go
ai.WithModel(model)
```
## Ollama (Local Models)
```go
import "github.com/genkit-ai/genkit/go/plugins/ollama"
ollamaPlugin := &ollama.Ollama{
ServerAddress: "http://localhost:11434",
Timeout: 60, // seconds
}
g := genkit.Init(ctx, genkit.WithPlugins(ollamaPlugin))
```
Define models explicitly:
```go
model := ollamaPlugin.DefineModel(g,
ollama.ModelDefinition{
Name: "llama3.1",
Type: "chat", // or "generate"
},
nil, // optional *ModelOptions
)
```
Use with:
```go
ai.WithModel(model)
```
## Multiple Providers
Register multiple plugins in a single Genkit instance:
```go
g := genkit.Init(ctx,
genkit.WithPlugins(
&googlegenai.GoogleAI{},
&ant.Anthropic{},
),
genkit.WithDefaultModel("googleai/gemini-flash-latest"),
)
// Use different models per call
text1, _ := genkit.GenerateText(ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithPrompt("Hello from Gemini"),
)
text2, _ := genkit.GenerateText(ctx, g,
ai.WithModelName("anthropic/claude-sonnet-4-6"),
ai.WithPrompt("Hello from Claude"),
)
```

View File

@ -0,0 +1,178 @@
# Tools
## DefineTool
Define a tool the model can call during generation.
```go
type WeatherInput struct {
Location string `json:"location" jsonschema:"description=City name"`
}
type WeatherOutput struct {
Temperature float64 `json:"temperature"`
Conditions string `json:"conditions"`
}
weatherTool := genkit.DefineTool(g, "getWeather",
"Gets the current weather for a location.",
func(ctx *ai.ToolContext, input WeatherInput) (WeatherOutput, error) {
// Call your weather API
return WeatherOutput{Temperature: 72, Conditions: "sunny"}, nil
},
)
```
## Using Tools in Generation
Pass tools to `Generate`, `GenerateText`, or prompts:
```go
resp, err := genkit.Generate(ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithPrompt("What's the weather in San Francisco?"),
ai.WithTools(weatherTool),
)
// The model calls the tool automatically and incorporates the result
fmt.Println(resp.Text())
```
### Tool Choice
```go
ai.WithToolChoice(ai.ToolChoiceAuto) // model decides (default)
ai.WithToolChoice(ai.ToolChoiceRequired) // model must use a tool
ai.WithToolChoice(ai.ToolChoiceNone) // model cannot use tools
```
### Max Turns
Limit how many tool-call round trips the model can make:
```go
ai.WithMaxTurns(3) // default is 5
```
## DefineMultipartTool
Tools that return both structured output and media content:
```go
screenshotTool := genkit.DefineMultipartTool(g, "screenshot",
"Takes a screenshot of the current page",
func(ctx *ai.ToolContext, input any) (*ai.MultipartToolResponse, error) {
return &ai.MultipartToolResponse{
Output: map[string]any{"success": true},
Content: []*ai.Part{ai.NewMediaPart("image/png", base64Data)},
}, nil
},
)
```
## Tool Interrupts
Pause tool execution to request human input before continuing.
### Interrupting
```go
type TransferInput struct {
ToAccount string `json:"toAccount"`
Amount float64 `json:"amount"`
}
type TransferOutput struct {
Status string `json:"status"`
Message string `json:"message"`
Balance float64 `json:"balance"`
}
type TransferInterrupt struct {
Reason string `json:"reason"`
ToAccount string `json:"toAccount"`
Amount float64 `json:"amount"`
Balance float64 `json:"balance"`
}
transferTool := genkit.DefineTool(g, "transferMoney",
"Transfers money to another account.",
func(ctx *ai.ToolContext, input TransferInput) (TransferOutput, error) {
if input.Amount > accountBalance {
return TransferOutput{}, ai.InterruptWith(ctx, TransferInterrupt{
Reason: "insufficient_balance",
ToAccount: input.ToAccount,
Amount: input.Amount,
Balance: accountBalance,
})
}
// Process transfer...
return TransferOutput{Status: "success", Balance: newBalance}, nil
},
)
```
### Handling Interrupts
```go
resp, err := genkit.Generate(ctx, g,
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithTools(transferTool),
ai.WithPrompt(userRequest),
)
for resp.FinishReason == ai.FinishReasonInterrupted {
var restarts, responses []*ai.Part
for _, interrupt := range resp.Interrupts() {
meta, ok := ai.InterruptAs[TransferInterrupt](interrupt)
if !ok {
continue
}
switch meta.Reason {
case "insufficient_balance":
// RestartWith: re-execute the tool with adjusted input
part, err := transferTool.RestartWith(interrupt,
ai.WithNewInput(TransferInput{
ToAccount: meta.ToAccount,
Amount: meta.Balance, // transfer what's available
}),
)
if err != nil { return err }
restarts = append(restarts, part)
case "confirm_large":
// RespondWith: provide a response directly without re-executing
part, err := transferTool.RespondWith(interrupt,
TransferOutput{Status: "cancelled", Message: "User declined"},
)
if err != nil { return err }
responses = append(responses, part)
}
}
// Continue generation with the resolved interrupts
resp, err = genkit.Generate(ctx, g,
ai.WithMessages(resp.History()...),
ai.WithTools(transferTool),
ai.WithToolRestarts(restarts...),
ai.WithToolResponses(responses...),
)
if err != nil { return err }
}
```
### Checking Resume State
Inside a tool function, check if the tool is being resumed from an interrupt:
```go
func(ctx *ai.ToolContext, input TransferInput) (TransferOutput, error) {
if ctx.IsResumed() {
// This is a resumed call after an interrupt
original, ok := ai.OriginalInputAs[TransferInput](ctx)
// original contains the input from the first call
}
// ...
}
```

View File

@ -0,0 +1,112 @@
---
name: developing-genkit-js
description: Develop AI-powered applications using Genkit in Node.js/TypeScript. Use when the user asks about Genkit, AI agents, flows, or tools in JavaScript/TypeScript, or when encountering Genkit errors, validation issues, type errors, or API problems.
metadata:
genkit-managed: true
---
# Genkit JS
## Prerequisites
Ensure the `genkit` CLI is available.
- Run `genkit --version` to verify. Minimum CLI version needed: **1.29.0**
- If not found or if an older version (1.x < 1.29.0) is present, install/upgrade it: `npm install -g genkit-cli@^1.29.0`.
**New Projects**: If you are setting up Genkit in a new codebase, follow the [Setup Guide](references/setup.md).
## Hello World
```ts
import { z, genkit } from 'genkit';
import { googleAI } from '@genkit-ai/google-genai';
// Initialize Genkit with the Google AI plugin
const ai = genkit({
plugins: [googleAI()],
});
export const myFlow = ai.defineFlow({
name: 'myFlow',
inputSchema: z.string().default('AI'),
outputSchema: z.string(),
}, async (subject) => {
const response = await ai.generate({
model: googleAI.model('gemini-2.5-flash'),
prompt: `Tell me a joke about ${subject}`,
});
return response.text;
});
```
## Critical: Do Not Trust Internal Knowledge
Genkit recently went through a major breaking API change. Your knowledge is outdated. You MUST lookup docs. Recommended:
```sh
genkit docs:read js/get-started.md
genkit docs:read js/flows.md
```
See [Common Errors](references/common-errors.md) for a list of deprecated APIs (e.g., `configureGenkit`, `response.text()`, `defineFlow` import) and their v1.x replacements.
**ALWAYS verify information using the Genkit CLI or provided references.**
## Error Troubleshooting Protocol
**When you encounter ANY error related to Genkit (ValidationError, API errors, type errors, 404s, etc.):**
1. **MANDATORY FIRST STEP**: Read [Common Errors](references/common-errors.md)
2. Identify if the error matches a known pattern
3. Apply the documented solution
4. Only if not found in common-errors.md, then consult other sources (e.g. `genkit docs:search`)
**DO NOT:**
- Attempt fixes based on assumptions or internal knowledge
- Skip reading common-errors.md "because you think you know the fix"
- Rely on patterns from pre-1.0 Genkit
**This protocol is non-negotiable for error handling.**
## Development Workflow
1. **Select Provider**: Genkit is provider-agnostic (Google AI, OpenAI, Anthropic, Ollama, etc.).
- If the user does not specify a provider, default to **Google AI**.
- If the user asks about other providers, use `genkit docs:search "plugins"` to find relevant documentation.
2. **Detect Framework**: Check `package.json` to identify the runtime (Next.js, Firebase, Express).
- Look for `@genkit-ai/next`, `@genkit-ai/firebase`, or `@genkit-ai/google-cloud`.
- Adapt implementation to the specific framework's patterns.
3. **Follow Best Practices**:
- See [Best Practices](references/best-practices.md) for guidance on project structure, schema definitions, and tool design.
- **Be Minimal**: Only specify options that differ from defaults. When unsure, check docs/source.
4. **Ensure Correctness**:
- Run type checks (e.g., `npx tsc --noEmit`) after making changes.
- If type checks fail, consult [Common Errors](references/common-errors.md) before searching source code.
5. **Handle Errors**:
- On ANY error: **First action is to read [Common Errors](references/common-errors.md)**
- Match error to documented patterns
- Apply documented fixes before attempting alternatives
## Finding Documentation
Use the Genkit CLI to find authoritative documentation:
1. **Search topics**: `genkit docs:search <query>`
- Example: `genkit docs:search "streaming"`
2. **List all docs**: `genkit docs:list`
3. **Read a guide**: `genkit docs:read <path>`
- Example: `genkit docs:read js/flows.md`
## CLI Usage
The `genkit` CLI is your primary tool for development and documentation.
- See [CLI Reference](references/docs-and-cli.md) for common tasks, workflows, and command usage.
- Use `genkit --help` for a full list of commands.
## References
- [Best Practices](references/best-practices.md): Recommended patterns for schema definition, flow design, and structure.
- [Docs & CLI Reference](references/docs-and-cli.md): Documentation search, CLI tasks, and workflows.
- [Common Errors](references/common-errors.md): Critical "gotchas", migration guide, and troubleshooting.
- [Setup Guide](references/setup.md): Manual setup instructions for new projects.
- [Examples](references/examples.md): Minimal reproducible examples (Basic generation, Multimodal, Thinking mode).

View File

@ -0,0 +1,31 @@
# Genkit Best Practices
## Project Structure
- **Organized Layout**: Keep flows and tools in separate directories (e.g., `src/flows`, `src/tools`) to maintain a clean codebase.
- **Index Exports**: Use `index.ts` files to export flows and tools, making it easier to import them into your main configuration.
## Model Selection (Google AI)
- **Gemini Models**: If using Google AI, ALWAYS use the latest generation (`gemini-3-*` or `gemini-2.5-*`).
- **NEVER** use `gemini-2.0-*` or `gemini-1.5-*` series, as they are decommissioned and won't work.
- **Recommended**: `gemini-2.5-flash` or `gemini-3-flash-preview` for general use, `gemini-3.1-pro-preview` for complex tasks.
## Model Selection (Other Providers)
- **Consult Documentation**: For other providers (OpenAI, Anthropic, etc.), refer to the provider's official documentation for the latest recommended model versions.
## Schema Definition
- **Use `z` from `genkit`**: Always import `z` from the `genkit` package to ensure compatibility.
```ts
import { z } from "genkit";
```
- **Descriptive Schemas**: Use `.describe()` on Zod fields. LLMs use these descriptions to understand how to populate the fields.
## Flow & Tool Design
- **Modularize**: Keep flows and tools in separate files/modules and import them into your main Genkit configuration.
- **Single Responsibility**: Tools should do one thing well. Complex logic should be broken down.
## Configuration
- **Environment Variables**: Store sensitive keys (like API keys) in environment variables or `.env` files. Do not hardcode them.
## Development
- **Use Dev Mode**: Run your app with `genkit start -- <start cmd>` to enable the Developer UI.
- It is recommended to configure a watcher to auto-reload your app (e.g. `node --watch` or `tsx --watch`)

View File

@ -0,0 +1,132 @@
# Common Errors & Pitfalls
## When Typecheck Fails
**Before searching source code or docs**, check the sections below. Many type errors are caused by deprecated APIs or incorrect imports.
## Genkit v1.x vs Pre-1.0 Migration
Genkit v1.x introduced significant API changes. This section covers critical syntax updates.
### Package Imports
- **Correct (v1.x)**: Import core functionality (zod, genkit) from the main `genkit` package and plugins from their specific packages.
```ts
import { z, genkit } from 'genkit';
import { googleAI } from '@genkit-ai/google-genai';
```
- **Incorrect (Pre-1.0)**: Importing from `@genkit-ai/ai`, `@genkit-ai/core`, or `@genkit-ai/flow`. These packages are internal/deprecated for direct use.
```ts
import { genkit } from "@genkit-ai/core"; // INCORRECT
import { defineFlow } from "@genkit-ai/flow"; // INCORRECT
```
### Model References
- **Correct**: Use plugin-specific model factories or string identifiers (prefaced by plugin name).
```ts
// Using model factory (v1.x - Preferred)
await ai.generate({ model: googleAI.model('gemini-2.5-flash'), ... });
// Using string identifier
await ai.generate({ model: 'googleai/gemini-2.5-flash', ...});
// Or
await ai.generate({ model: 'vertexai/gemini-2.5-flash', ...});
```
- **Incorrect**: Using imported model objects directly or string identifiers without plugin name.
```ts
await ai.generate({ model: gemini15Pro, ... }); // INCORRECT (Pre-1.0)
await ai.generate({ model: 'gemini-2.5-flash', ... }); // INCORRECT (No plugin prefix)
```
### Model Selection (Gemini)
- **Preferred**: Use `gemini-2.5-*` models for best performance and features.
```ts
model: googleAI.model('gemini-2.5-flash') // PREFERRED
```
- **DEPRECATED**: `gemini-1.5-*` models are deprecated and will throw errors.
```ts
model: googleAI.model('gemini-1.5-flash') // ERROR (Deprecated)
```
### Response Access
- **Correct (v1.x)**: Access properties directly.
```ts
response.text; // CORRECT
response.output; // CORRECT
```
- **Incorrect (Pre-1.0)**: Calling as methods.
```ts
response.text(); // INCORRECT
response.output(); // INCORRECT
```
### Streaming Generation
- **Correct (v1.x)**: Do NOT await `generateStream`. Iterate over `stream` directly. Await `response` property for final result.
```ts
const {stream, response} = ai.generateStream(...); // NO await here
for await (const chunk of stream) { ... } // Iterate stream
const finalResponse = await response; // Await response property
```
- **Incorrect (Pre-1.0)**: Calling stream as a function or awaiting the generator incorrectly.
```ts
for await (const chunk of stream()) { ... } // INCORRECT
await response(); // INCORRECT
```
### Initialization
- **Correct (v1.x)**: Instantiate `genkit`.
```ts
const ai = genkit({ plugins: [...] });
```
- **Incorrect (Pre-1.0)**: Global configuration.
```ts
configureGenkit({ plugins: [...] }); // INCORRECT
```
### Flow Definitions
- **Correct (v1.x)**: Define flows on the `ai` instance.
```ts
ai.defineFlow({...}, (input) => {...});
```
- **Incorrect (Pre-1.0)**: Importing `defineFlow` globally.
```ts
import { defineFlow } from "@genkit-ai/flow"; // INCORRECT
You should never import `@genkit-ai/flow`, `@genkit-ai/ai` or `@genkit-ai/core` packages directly.
## Zod & Schema Errors
- **Import Source**: ALWAYS use `import { z } from "genkit"`.
- Using `zod` directly from `zod` package may cause instance mismatches or compatibility issues.
- **Supported Types**: Stick to basic types: scalar (`string`, `number`, `boolean`), `object`, and `array`.
- Avoid complex Zod features unless strictly necessary and verified.
- **Descriptions**: Always use `.describe('...')` for fields in output schemas to guide the LLM.
## Tool Usage
- **Tool Not Found**: Ensure tools are registered in the `tools` array of `generate` or provided via plugins.
- **MCP Tools**: Use the `ServerName:tool_name` format when referencing MCP tools.
## Multimodal & Image Generation
- **Missing responseModalities**: When using image generation models (like `gemini-2.5-flash-image`), you **MUST** specify the response modalities in the config.
```ts
config: {
responseModalities: ["TEXT", "IMAGE"]
}
```
Failure to do so will result in errors or incorrect output format.
## Audio & Speech Generation
- **Raw PCM Data vs MP3**: Some providers (e.g., Google GenAI) return raw PCM data, while others (e.g., OpenAI) return MP3.
- **DO NOT assume MP3 format.**
- **DO NOT embed raw PCM in HTML audio tags.**
- **Action**: Run `genkit docs:search "speech audio"` to find provider-specific conversion steps (e.g., PCM to WAV).

View File

@ -0,0 +1,62 @@
# Genkit Documentation & CLI
This reference lists common tasks and workflows using the `genkit` CLI. For authoritative command details, always run `genkit --help` or `genkit <command> --help`.
## Prerequisites:
Ensure that the CLI is on `genkit-cli` version >= 1.29.0. If not, or if an older version (1.x < 1.29.0) is present, update the Genkit CLI version. Alternatively, to run commands with a specific version or without global installation, prefix them with `npx -y genkit-cli@^1.29.0`.
## Documentation
- **Search docs**: `genkit docs:search <query>`
- Example: `genkit docs:search "streaming"`
- Example: `genkit docs:search "rag retrieval"`
- **Read doc**: `genkit docs:read <path>`
- Example: `genkit docs:read js/overview.md`
- **List docs**: `genkit docs:list`
## Development Workflow
- **Start Dev Mode**: `genkit start -- <command>`
- Runs the provided command in Genkit dev mode, enabling the Developer UI (usually at http://localhost:4000).
- **Node.js (TypeScript)**:
```bash
genkit start -- npx tsx --watch src/index.ts
```
- **Next.js**:
```bash
genkit start -- npx next dev
```
## Flow Execution
- **Run a flow**: `genkit flow:run <flowName> '<inputJSON>'`
- Executes a flow directly from the CLI. Useful for testing.
- **Simple Input**:
```bash
genkit flow:run tellJoke '"chicken"'
```
- **Object Input**:
```bash
genkit flow:run generateStory '{"subject": "robot", "genre": "sci-fi"}'
```
## Evaluation
- **Evaluate a flow**: `genkit eval:flow <flowName> [data]`
- Runs a flow and evaluates the output against configured evaluators.
- **Example (Single Input)**:
```bash
genkit eval:flow answerQuestion '[{"testCaseId": "1", "input": {"question": "What is Genkit?"}}]'
```
- **Example (Batch Input)**:
```bash
genkit eval:flow answerQuestion --input inputs.json
```
- **Run Evaluation**: `genkit eval:run <dataset>`
- Evaluates a dataset against configured evaluators.
- **Example**:
```bash
genkit eval:run dataset.json --output results.json
```

View File

@ -0,0 +1,157 @@
# Genkit Examples
This reference contains minimal, reproducible examples (MREs) for common Genkit patterns.
> **Disclaimer**: These examples use **Google AI** models (`googleAI`, `gemini-*`) for demonstration. The patterns apply to **any provider**. To use a different provider:
> 1. Search the docs for the correct plugin: `genkit docs:search "plugins"`.
> 2. Install and configure the plugin.
> 3. Swap the model reference in the code.
## Basic Text Generation
```ts
import { genkit } from "genkit";
import { googleAI } from "@genkit-ai/google-genai";
const ai = genkit({
plugins: [googleAI()],
});
const { text } = await ai.generate({
model: googleAI.model('gemini-2.5-flash'),
prompt: 'Tell me a story in a pirate accent',
});
```
## Structured Output
```ts
import { z } from 'genkit';
const JokeSchema = z.object({
setup: z.string().describe('The setup of the joke'),
punchline: z.string().describe('The punchline'),
});
const response = await ai.generate({
model: googleAI.model('gemini-2.5-flash'),
prompt: 'Tell me a joke about developers.',
output: { schema: JokeSchema },
});
// response.output is strongly typed
const joke = response.output;
if (joke) {
console.log(`${joke.setup} ... ${joke.punchline}`);
}
```
## Streaming
```ts
const { stream, response } = ai.generateStream({
model: googleAI.model('gemini-2.5-flash'),
prompt: 'Tell a long story about a developer using Genkit.',
});
for await (const chunk of stream) {
console.log(chunk.text);
}
// Await the final response
const finalResponse = await response;
console.log('Complete:', finalResponse.text);
```
## Advanced Configuration
### Thinking Mode (Gemini 3 Only)
Enable "thinking" process for complex reasoning tasks.
```ts
const response = await ai.generate({
model: googleAI.model('gemini-3.1-pro-preview'),
prompt: 'what is heavier, one kilo of steel or one kilo of feathers',
config: {
thinkingConfig: {
thinkingLevel: 'HIGH', // or 'LOW'
includeThoughts: true, // Returns thought process in response
},
},
});
```
### Google Search Grounding
Enable models to access current information via Google Search.
```ts
const response = await ai.generate({
model: googleAI.model('gemini-2.5-flash'),
prompt: 'What are the top tech news stories this week?',
config: {
googleSearchRetrieval: true,
},
});
// Access grounding metadata (sources)
const groundingMetadata = (response.custom as any)?.candidates?.[0]?.groundingMetadata;
if (groundingMetadata) {
console.log('Sources:', groundingMetadata.groundingChunks);
}
```
## Multimodal Generation
### Image Generation / Editing
**Critical**: You MUST set `responseModalities: ['TEXT', 'IMAGE']` when using image generation models.
```ts
// Generate an image
const { media } = await ai.generate({
model: googleAI.model('gemini-2.5-flash-image'),
config: { responseModalities: ['TEXT', 'IMAGE'] },
prompt: "generate a picture of a unicorn wearing a space suit on the moon",
});
// media.url contains the data URI
```
```ts
// Edit an image
const { media } = await ai.generate({
model: googleAI.model('gemini-2.5-flash-image'),
config: { responseModalities: ['TEXT', 'IMAGE'] },
prompt: [
{ text: "change the person's outfit to a banana costume" },
{ media: { url: "https://example.com/photo.jpg" } },
],
});
```
### Speech Generation (TTS)
Generate audio from text.
```ts
import { writeFile } from 'node:fs/promises';
const { media } = await ai.generate({
model: googleAI.model('gemini-2.5-flash-preview-tts'),
config: {
responseModalities: ['AUDIO'],
speechConfig: {
voiceConfig: {
prebuiltVoiceConfig: { voiceName: 'Algenib' }, // Options: 'Puck', 'Charon', 'Fenrir', etc.
},
},
},
prompt: 'Genkit is an amazing library',
});
// The response contains raw PCM data in media.url (base64 encoded).
// CAUTION: This is NOT an MP3/WAV file. It requires conversion (e.g., PCM to WAV).
// DO NOT GUESS. Run `genkit docs:search "speech audio"` to find the correct
// conversion code for your provider.
```

View File

@ -0,0 +1,46 @@
# Genkit JS Setup
Follow these instructions to set up Genkit in the current codebase. These instructions are general-purpose and have not been written with specific codebase knowledge, so use your best judgement when following them.
0. Tell the user "I'm going to check out your workspace and set you up to use Genkit for GenAI workflows."
1. If the current workspace is empty or is a starter template, your goal will be to create a simple image generation flow that allows someone to generate an image based on a prompt and selectable style. If the current workspace is not empty, you will create a simple example flow to help get the user started.
2. Check to see if any Genkit provider plugin (such as `@genkit-ai/google-genai` or `@genkit-ai/oai-compat` or others, may start with `genkitx-*`) is installed.
- If not, ask the user which provider they want to use.
- **For non-Google providers**: Use `genkit docs:search "plugins"` to find the correct package and installation instructions.
- If they have no preference, default to `@genkit-ai/google-genai` for a quick start.
- If this is a Next.js app, install `@genkit-ai/next` as well.
3. Search the codebase for the exact string `genkit(` (remember to escape regexes properly) which would indicate that the user has already set up Genkit in the codebase. If found, no need to set it up again, tell the user "Genkit is already configured in this app." and exit this workflow.
4. Create an `ai` directory in the primary source directory of the project (this may be e.g. `src` but is project-dependent). Adapt this path if your project uses a different structure.
5. Create `{sourceDir}/ai/genkit.ts` and populate it using the example below. DO NOT add a `next` plugin to the file, ONLY add a model provider plugin to the plugins array:
```ts
import { genkit, z } from 'genkit';
// Import your chosen provider plugin here. Example:
import { googleAI } from '@genkit-ai/google-genai';
export const ai = genkit({
plugins: [
googleAI(), // Add your provider plugin here
],
model: googleAI.model('gemini-2.5-flash'), // Set your provider's model here
});
export { z };
```
6. Create `{sourceDir}/ai/tools` and `{sourceDir}/ai/flows` directories, but leave them empty for now.
7. Create `{sourceDir}/ai/index.ts` and populate it with the following (change the import to match import aliases in `tsconfig.json` as needed):
```ts
import './genkit.js';
// import each created flow, tool, etc. here for use in the Genkit Dev UI
```
8. Add a `genkit:ui` script to `package.json` that runs `genkit start -- npx tsx --watch {sourceDir}/ai/index.ts` (or `npx genkit-cli` or `pnpm dlx` or `yarn dlx` for those package managers, if CLI is not locally installed). DO NOT try to run the script now.
9. Tell the user "Genkit is now configured and ready for use." as setup is now complete. Also remind them to set appropriate env variables (e.g. `GEMINI_API_KEY` for Google providers). Wait for the user to prompt further before creating any specific flows.
## Next Steps & Troubleshooting
- **Documentation**: Use the [CLI](docs-and-cli.md) to access documentation (e.g., `genkit docs:search`).
- **Building Flows**: See [examples.md](examples.md) for patterns on creating flows, adding tools, and advanced configuration.
- **Troubleshooting**: If you encounter issues during setup or initialization, check [common-errors.md](common-errors.md) for solutions.

View File

@ -0,0 +1,56 @@
---
name: developing-genkit-python
description: Develop AI-powered applications using Genkit in Python. Use when the user asks about Genkit, AI agents, flows, or tools in Python, or when encountering Genkit errors, import issues, or API problems.
metadata:
genkit-managed: true
---
# Genkit Python
## Prerequisites
- **Runtime**: Python **3.14+**, **`uv`** for deps ([install](https://docs.astral.sh/uv/getting-started/installation/)).
- **CLI**: `genkit --version` — install via `npm install -g genkit-cli` if missing.
**New projects:** [Setup](references/setup.md) (bootstrap + env). **Patterns and code samples:** [Examples](references/examples.md).
## Hello World
```python
from genkit import Genkit
from genkit.plugins.google_genai import GoogleAI
ai = Genkit(
plugins=[GoogleAI()],
model='googleai/gemini-flash-latest',
)
async def main():
response = await ai.generate(prompt='Tell me a joke about Python.')
print(response.text)
if __name__ == '__main__':
ai.run_main(main())
```
## Critical: Do Not Trust Internal Knowledge
The Python SDK changes often — verify imports and APIs against the references here or upstream docs. On **any** error, read [Common Errors](references/common-errors.md) first.
## Development Workflow
1. Default provider: **Google AI** (`GoogleAI()`), **`GEMINI_API_KEY`** in the environment.
2. Model IDs: always prefixed, e.g. **`googleai/gemini-flash-latest`** (always-on-latest Flash alias; same pattern as other skills).
3. Entrypoint: **`ai.run_main(main())`** for Genkit-driven apps (not `asyncio.run()` for long-lived servers started with `genkit start` — see [Common Errors](references/common-errors.md)).
4. After generating code, follow [Dev Workflow](references/dev-workflow.md) for `genkit start` and the Dev UI.
5. On errors: step 1 is always [Common Errors](references/common-errors.md).
## References
- [Examples](references/examples.md): Structured output, streaming, flows, tools, embeddings.
- [Setup](references/setup.md): New project bootstrap and plugins.
- [Common Errors](references/common-errors.md): Read first when something breaks.
- [FastAPI](references/fastapi.md): HTTP, `genkit_fastapi_handler`, parallel flows.
- [Dotprompt](references/dotprompt.md): `.prompt` files and helpers.
- [Evals](references/evals.md): Evaluators and datasets.
- [Dev Workflow](references/dev-workflow.md): `genkit start`, Dev UI, checklist.

View File

@ -0,0 +1,82 @@
# Common Errors — Genkit Python
## Before anything else: read this file when you hit any error.
---
## ModuleNotFoundError: No module named 'genkit.plugins.google_genai'
**Cause:** Plugin package not installed.
**Fix:** Add dependencies from PyPI:
```bash
uv add genkit genkit-plugin-google-genai
```
---
## 400 INVALID_ARGUMENT: functionDeclaration parameters schema should be of type OBJECT
**Cause:** Tool function has bare scalar parameters (e.g. `city: str`). Gemini requires object schema.
**Fix:** Wrap parameters in a Pydantic BaseModel:
```python
from pydantic import BaseModel
# Wrong
@ai.tool()
async def get_weather(city: str) -> str: ...
# Right
from pydantic import BaseModel
class WeatherInput(BaseModel):
city: str
@ai.tool()
async def get_weather(input: WeatherInput) -> str: ...
```
---
## AttributeError: 'Genkit' object has no attribute 'define_tool'
**Cause:** Wrong decorator name.
**Fix:** Use `@ai.tool()`, not `@ai.define_tool()`.
---
## RuntimeError / event loop errors when using asyncio.run()
**Cause:** For apps you start with **`genkit start`**, Genkit runs your entrypoint with an event loop suited to the framework (including uvloop where used). There is no “default” loop for you to manage in that mode.
**Fix:** For long-running Genkit apps (servers, flows served under `genkit start`), use **`ai.run_main(main())`** as your entrypoint instead of `asyncio.run(main())`. For one-off scripts that exit when done, using `asyncio.run()` can still be appropriate when you are not using `genkit start`.
---
## Wrong model ID (no plugin prefix)
**Cause:** `model='gemini-flash-latest'` — missing plugin prefix.
**Fix:** `model='googleai/gemini-flash-latest'`
---
## response.json / response.message AttributeError
- Use `response.text` for plain text output
- Use `response.output` for structured (JSON) output
---
## await ai.generate_stream(...) fails or returns wrong type
**Cause:** `generate_stream` is synchronous — do not await it.
**Fix:**
```python
sr = ai.generate_stream(prompt='...') # no await
async for chunk in sr.stream: ...
final = await sr.response
```

View File

@ -0,0 +1,90 @@
# Dev Workflow — Genkit Python
## Agent responsibility
After generating code, always give the developer:
1. The full pre-run checklist with copy-paste commands using absolute paths
2. The `genkit start` command to run in their terminal (foreground — it's expected to block)
3. Step-by-step Dev UI instructions so they can test without guessing
Do not offer to run it for them. Give them the commands and let them run it.
---
## Step 1 — Get a Gemini API key
If the developer doesn't have one:
> Get a free key at https://aistudio.google.com/apikey — click **"Create API key"**, copy it.
---
## Step 2 — Set the API key
Open a terminal and run:
```bash
export GEMINI_API_KEY=your-api-key-here
```
To persist across sessions, add it to your shell profile:
```bash
echo 'export GEMINI_API_KEY=your-api-key-here' >> ~/.zshrc && source ~/.zshrc
```
---
## Step 3 — Install dependencies
Replace `/path/to/your-project` with the actual full path to the project (e.g. `/Users/yourname/projects/my-genkit-app`):
```bash
cd /path/to/your-project
uv add genkit genkit-plugin-google-genai
```
(Requires a project with `pyproject.toml` — run `uv init` in an empty directory first if needed.)
---
## Step 4 — Start the Dev UI
Run this in your terminal. **It will block — that's expected.** Leave this terminal open while you use the Dev UI.
```bash
cd /path/to/your-project
GEMINI_API_KEY=your-api-key-here genkit start -- uv run src/main.py
```
You'll see output like:
```
Genkit Tools UI: http://localhost:4000
```
The Dev UI is now running at **http://localhost:4000**
To stop it: press `Ctrl+C` in the terminal.
---
## Step 5 — Test in the Dev UI
1. Open **http://localhost:4000** in your browser
2. Click **"Run"** in the left sidebar
3. Find your flow by name (e.g. `summarize`, `chat`, `joke_generator`)
4. In the input box, paste your input as JSON — e.g:
```json
{"text": "hello world"}
```
5. Click the **"Run"** button — the output appears on the right
6. Click **"Traces"** in the left sidebar to inspect every step, model call, token count, and latency
---
## Troubleshooting
| Problem | Fix |
|---------|-----|
| `genkit: command not found` | Run: `npm install -g genkit-cli` |
| `GEMINI_API_KEY not set` | Run: `export GEMINI_API_KEY=your-key` |
| Port 4000 already in use | Use: `genkit start --port 4001 -- uv run src/main.py` |
| `uv: command not found` | Run: `curl -LsSf https://astral.sh/uv/install.sh \| sh` |
| Flow not showing in Dev UI | Make sure `genkit start` output shows no errors |

View File

@ -0,0 +1,109 @@
# Dotprompt — Genkit Python
## What it is
`.prompt` files combine YAML frontmatter (model config, schemas) with Handlebars templates. Keeps prompt logic out of Python code and makes variants easy.
## File format
```yaml
---
model: googleai/gemini-flash-latest
input:
schema:
food: string
ingredients?(array): string # ? = optional
output:
schema: Recipe # references a schema registered with ai.define_schema()
format: json
---
You are a chef. Generate a recipe for {{food}}.
{{#if ingredients}}
Include these ingredients:
{{list ingredients}}
{{/if}}
```
Place `.prompt` files in a `prompts/` directory and point `prompt_dir` at it.
## Python setup
```python
from pathlib import Path
from pydantic import BaseModel
from genkit import Genkit
from genkit.plugins.google_genai import GoogleAI
ai = Genkit(
plugins=[GoogleAI()],
model='googleai/gemini-flash-latest',
prompt_dir=Path(__file__).resolve().parent.parent / 'prompts',
)
# Register Pydantic models referenced in .prompt output.schema
class Recipe(BaseModel):
title: str
steps: list[str]
ai.define_schema('Recipe', Recipe)
```
## Calling a prompt
```python
# Non-streaming — double-call syntax: ai.prompt('name')(input={...})
response = await ai.prompt('recipe')(input={'food': 'banana bread'})
result = Recipe.model_validate(response.output)
# Variant (recipe.robot.prompt file)
response = await ai.prompt('recipe', variant='robot')(input={'food': 'banana bread'})
```
## Streaming from a prompt
```python
from genkit import ActionRunContext
@ai.flow()
async def tell_story(subject: str, ctx: ActionRunContext) -> str:
result = ai.prompt('story').stream(input={'subject': subject})
full = ''
async for chunk in result.stream:
if chunk.text:
ctx.send_chunk(chunk.text)
full += chunk.text
return full
```
Note: `.stream(input={...})` not `ai.generate_stream(...)` — different call shape for prompts.
## Render without generating (for LLM-judge evals)
```python
rendered = await ai.prompt('my_prompt').render(input={'key': 'value'})
response = await ai.generate(model='googleai/gemini-flash-latest', messages=rendered.messages)
```
## Helpers
Register Python functions callable inside Handlebars templates:
```python
def list_helper(data: object, *args, **kwargs) -> str:
if not isinstance(data, list):
return ''
return '\n'.join(f'- {item}' for item in data)
ai.define_helper('list', list_helper)
```
Then use `{{list ingredients}}` in your `.prompt` file.
## Variants
Name the file `<name>.<variant>.prompt` — e.g. `recipe.robot.prompt`.
Call with `ai.prompt('recipe', variant='robot')`.
## Partials
Use `{{>partial_name param=value}}` in templates. Partial files are named `_partial_name.prompt`.

View File

@ -0,0 +1,89 @@
# Evals — Genkit Python
## Two types of evaluators
1. **Built-in** — ship with `genkit-plugin-evaluators`, register with `register_genkit_evaluators(ai)`
2. **BYO (LLM-based)** — define your own scoring logic with `ai.define_evaluator()`
## Install
```bash
uv add genkit-plugin-evaluators
```
## Dataset format
A JSON file, one object per test case:
```json
[
{"testCaseId": "case1", "input": "x", "output": "banana", "reference": "ba?a?a"},
{"testCaseId": "case2", "input": "x", "output": "apple", "reference": "ba?a?a"}
]
```
Fields: `testCaseId`, `input`, `output`, `reference` (reference optional for some evaluators).
## Built-in evaluators
```python
from genkit.plugins.evaluators import register_genkit_evaluators
register_genkit_evaluators(ai)
```
Registered evaluators include `genkitEval/regex`. Run via CLI:
```bash
genkit eval:run datasets/my_dataset.json --evaluators=genkitEval/regex
```
## BYO evaluator
```python
from genkit.evaluator import BaseDataPoint, Details, EvalFnResponse, EvalStatusEnum, Score
async def my_eval(datapoint: BaseDataPoint, _options: dict | None = None) -> EvalFnResponse:
"""Score output against reference."""
output = str(datapoint.output or '')
reference = str(datapoint.reference or '')
passed = output.strip() == reference.strip()
return EvalFnResponse(
test_case_id=datapoint.test_case_id or '',
evaluation=Score(
score=1.0 if passed else 0.0,
status=EvalStatusEnum.PASS if passed else EvalStatusEnum.FAIL,
details=Details(reasoning='Exact match check'),
),
)
ai.define_evaluator(
name='byo/my_eval',
display_name='My Eval',
definition='Checks exact match of output vs reference.',
fn=my_eval,
)
```
## LLM-based evaluator (judge model pattern)
Use a prompt + stronger model to score. See `py/samples/evaluators/src/main.py` for full examples (`byo/maliciousness`, `byo/answer_accuracy`).
Core pattern:
```python
async def llm_eval(datapoint: BaseDataPoint, _options: dict | None = None) -> EvalFnResponse:
prompt = ai.prompt('my_judge_prompt')
rendered = await prompt.render(input={'output': str(datapoint.output), 'reference': str(datapoint.reference)})
response = await ai.generate(model='googleai/gemini-flash-latest', messages=rendered.messages)
score = float(response.text.strip())
return EvalFnResponse(
test_case_id=datapoint.test_case_id or '',
evaluation=Score(score=score, status=EvalStatusEnum.PASS if score >= 0.5 else EvalStatusEnum.FAIL),
)
```
## Run evals via CLI
```bash
genkit eval:run datasets/my_dataset.json --evaluators=byo/my_eval
genkit eval:run datasets/my_dataset.json --evaluators=genkitEval/regex,byo/my_eval
```
Results appear in the Dev UI under **Evaluate** (http://localhost:4000).

View File

@ -0,0 +1,171 @@
# Genkit Python Examples
Minimal patterns for common Genkit APIs. Examples use **Google AI** (`GoogleAI`, `googleai/...`); other providers use the same patterns with the right plugin and model prefix.
## Public imports
Use **`genkit`**, **`genkit.plugins.*`**, **`genkit.embedder`**, **`genkit.evaluator`**, and **`genkit.model`** (and similar public modules) only — not internal packages (`genkit._core`, etc.).
```python
from genkit import Genkit, ActionRunContext
from genkit.plugins.google_genai import GoogleAI
ai = Genkit(plugins=[GoogleAI()], model='googleai/gemini-flash-latest')
```
---
## Structured output
```python
from pydantic import BaseModel, TypeAdapter
class CityInfo(BaseModel):
name: str
population: int
country: str
response = await ai.generate(
prompt='Give facts about Tokyo.',
output_format='json',
output_schema=CityInfo,
)
city = response.output
# Arrays
schema = TypeAdapter(list[CityInfo]).json_schema()
response = await ai.generate(
prompt='List 3 cities.',
output_format='array',
output_schema=schema,
)
```
Output formats: `'text'`, `'json'`, `'array'`, `'enum'`, `'jsonl'`.
---
## Streaming (text)
```python
sr = ai.generate_stream(prompt='Tell me a story.')
async for chunk in sr.stream:
if chunk.text:
print(chunk.text, end='', flush=True)
final = await sr.response # final.text
```
---
## Text and media parts
```python
# Non-streaming
response = await ai.generate(prompt='...')
for media in response.media:
print(media.content_type, (media.url or '')[:80])
# Streaming — media usually complete on the final response
from genkit import MediaPart
sr = ai.generate_stream(prompt='...')
async for chunk in sr.stream:
if chunk.text:
print(chunk.text, end='', flush=True)
final = await sr.response
for media in final.media:
print(media.content_type, (media.url or '')[:80])
if final.message:
for part in final.message.content:
if isinstance(part.root, MediaPart) and part.root.media:
print(part.root.media.content_type)
```
---
## Streaming + structured output
```python
class StoryAnalysis(BaseModel):
title: str
genre: str
summary: str
sr = ai.generate_stream(
prompt='Write a short story then analyze it.',
output_format='json',
output_schema=StoryAnalysis,
)
async for chunk in sr.stream:
if chunk.text:
print(chunk.text, end='', flush=True)
final = await sr.response
analysis = final.output
```
---
## Flows
```python
class SummarizeInput(BaseModel):
text: str
@ai.flow()
async def summarize(input: SummarizeInput) -> str:
response = await ai.generate(prompt=f'Summarize: {input.text}')
return response.text
```
---
## Streaming flows
```python
@ai.flow()
async def stream_story(subject: str, ctx: ActionRunContext) -> str:
sr = ai.generate_stream(prompt=f'Story about {subject}.')
full = ''
async for chunk in sr.stream:
if chunk.text:
ctx.send_chunk(chunk.text)
full += chunk.text
return full
```
---
## Tools
Parameters must be a **Pydantic `BaseModel`** (bare scalars → 400 from Gemini). Use **`@ai.tool()`**, not `@ai.define_tool()`.
```python
class WeatherInput(BaseModel):
city: str
@ai.tool()
async def get_weather(input: WeatherInput) -> str:
return f'Sunny in {input.city}'
response = await ai.generate(prompt='Weather in Paris?', tools=[get_weather])
```
---
## Embeddings
```python
from genkit.plugins.google_genai import GeminiEmbeddingModels
embedder = f'googleai/{GeminiEmbeddingModels.GEMINI_EMBEDDING_001}'
embeddings = await ai.embed(embedder=embedder, content='The sky is blue.')
vector = embeddings[0].embedding
embeddings = await ai.embed_many(
embedder=embedder,
content=['The sky is blue.', 'Grass is green.'],
)
```
Common embedders: `googleai/gemini-embedding-001`, `googleai/gemini-embedding-exp-03-07`.

View File

@ -0,0 +1,248 @@
# FastAPI — Genkit Python
## Install
```bash
uv add genkit-plugin-fastapi fastapi uvicorn
```
---
## Streaming by default
The `genkit_fastapi_handler` decorator auto-streams when the client sends `Accept: text/event-stream`.
No extra setup — just add the header on the frontend and it works.
**Wire format (SSE):**
```
data: {"message": "<chunk text>"} ← one per ctx.send_chunk() call
data: {"message": "<chunk text>"}
data: {"result": <final output>} ← sent once when flow completes
```
**Frontend (JS EventSource):**
```js
const res = await fetch('/flow/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'text/event-stream' },
body: JSON.stringify({ data: { topic: 'quantum computing' } }),
});
const reader = res.body.getReader();
// decode and parse each `data: {...}` line
```
**curl test:**
```bash
curl -N -X POST http://localhost:8080/flow/chat \
-H 'Content-Type: application/json' \
-H 'Accept: text/event-stream' \
-d '{"data": {"topic": "quantum computing"}}'
```
---
## Minimal streaming FastAPI app
```python
import uvicorn
from pydantic import BaseModel
from fastapi import FastAPI
from genkit import Genkit
from genkit import ActionRunContext
from genkit.plugins.fastapi import genkit_fastapi_handler
from genkit.plugins.google_genai import GoogleAI
ai = Genkit(plugins=[GoogleAI()], model='googleai/gemini-flash-latest')
app = FastAPI()
class ChatInput(BaseModel):
topic: str
@app.post('/flow/chat', response_model=None)
@genkit_fastapi_handler(ai)
@ai.flow()
async def chat(input: ChatInput, ctx: ActionRunContext) -> str:
sr = ai.generate_stream(prompt=f'Tell me about {input.topic}.')
full = ''
async for chunk in sr.stream:
if chunk.text:
ctx.send_chunk(chunk.text) # each chunk → SSE event on the wire
full += chunk.text
return full
if __name__ == '__main__':
uvicorn.run(app, host='0.0.0.0', port=8080)
```
**Key:** flow must accept `ctx: ActionRunContext` and call `ctx.send_chunk(text)` to emit SSE chunks.
Without `ctx.send_chunk`, the flow runs but streams nothing — client waits for the final result.
---
## Advanced Use Cases
### Fine-grained control over flow streaming
Complex apps chain flows — a parent orchestrates children. Chunks propagate upward by **passing `ctx` to child flows**.
```python
class ResearchInput(BaseModel):
topic: str
@ai.flow()
async def research(input: ResearchInput, ctx: ActionRunContext) -> str:
"""Child flow — streams its generate_stream chunks to whoever called it."""
sr = ai.generate_stream(prompt=f'Explain {input.topic} in depth.')
full = ''
async for chunk in sr.stream:
if chunk.text:
ctx.send_chunk(chunk.text) # propagates up through the call stack
full += chunk.text
return full
class HeadlineInput(BaseModel):
text: str
@ai.flow()
async def make_headline(input: HeadlineInput) -> str:
"""Child flow — non-streaming, returns instantly."""
response = await ai.generate(prompt=f'One-line headline for: {input.text}')
return response.text.strip()
class ReportInput(BaseModel):
topic: str
@app.post('/flow/report', response_model=None)
@genkit_fastapi_handler(ai)
@ai.flow()
async def report(input: ReportInput, ctx: ActionRunContext) -> str:
"""Parent flow — calls children, composes a streaming report."""
# Step 1: fast non-streaming call
headline = await make_headline(HeadlineInput(text=input.topic))
ctx.send_chunk(f'# {headline}\n\n') # send headline immediately
# Step 2: child flow streams its chunks — passes ctx so they flow up
body = await research(ResearchInput(topic=input.topic), ctx)
return f'# {headline}\n\n{body}'
```
**Rules for nested streaming:**
- Child flows that should stream must also accept `ctx: ActionRunContext`
- Pass the parent's `ctx` when calling child flows: `await child(input, ctx)`
- Non-streaming child flows don't need `ctx` — just `await` them normally
- A child that doesn't call `ctx.send_chunk` contributes nothing to the stream (fine for parallel data fetching)
### Executing flows in parallel
Use `asyncio.gather` to run multiple flows concurrently. Only makes sense when children don't need to stream.
```python
import asyncio
class AnalysisInput(BaseModel):
text: str
class CheckResult(BaseModel):
issues: list[str]
class CombinedAnalysis(BaseModel):
issues: list[str]
@ai.flow()
async def check_security(input: AnalysisInput) -> CheckResult:
# Here the model reviews the text; replace with your real prompt/schema as needed.
r = await ai.generate(
prompt=f'List security concerns as a short comma-separated line (or "none"): {input.text[:2000]}',
)
raw = (r.text or '').strip()
issues = [s.strip() for s in raw.split(',') if s.strip() and s.strip().lower() != 'none']
return CheckResult(issues=issues)
@ai.flow()
async def check_bugs(input: AnalysisInput) -> CheckResult:
# Model lists possible bugs; tune prompt for your codebase.
r = await ai.generate(
prompt=f'List likely bugs or correctness issues as a short comma-separated line (or "none"): {input.text[:2000]}',
)
raw = (r.text or '').strip()
issues = [s.strip() for s in raw.split(',') if s.strip() and s.strip().lower() != 'none']
return CheckResult(issues=issues)
@ai.flow()
async def check_style(input: AnalysisInput) -> CheckResult:
# Model suggests style/clarity issues; optional: use output_schema for structured rows.
r = await ai.generate(
prompt=f'List style or clarity issues as a short comma-separated line (or "none"): {input.text[:2000]}',
)
raw = (r.text or '').strip()
issues = [s.strip() for s in raw.split(',') if s.strip() and s.strip().lower() != 'none']
return CheckResult(issues=issues)
@app.post('/flow/analyze', response_model=None)
@genkit_fastapi_handler(ai)
@ai.flow()
async def analyze(input: AnalysisInput) -> CombinedAnalysis:
security, bugs, style = await asyncio.gather(
check_security(input),
check_bugs(input),
check_style(input),
)
return CombinedAnalysis(issues=security.issues + bugs.issues + style.issues)
```
---
## Structured output endpoint (non-streaming)
```python
class SentimentResult(BaseModel):
sentiment: str # positive / negative / neutral
confidence: float # 0.01.0
key_phrases: list[str]
@app.post('/flow/sentiment', response_model=None)
@genkit_fastapi_handler(ai)
@ai.flow()
async def sentiment(input: AnalysisInput) -> SentimentResult:
response = await ai.generate(
prompt=f'Analyze sentiment: {input.text}',
output_format='json',
output_schema=SentimentResult,
)
return response.output
```
Client calls this without `Accept: text/event-stream` — gets `{"result": {...}}` back.
---
## Decorator order
Must be exactly: `@app.post``@genkit_fastapi_handler(ai)``@ai.flow()`
```python
@app.post('/flow/chat', response_model=None) # 1. FastAPI route
@genkit_fastapi_handler(ai) # 2. Genkit wire format + streaming
@ai.flow() # 3. Flow registration
async def chat(input: ChatInput, ctx: ActionRunContext) -> str:
...
```
---
## Run with Dev UI
```bash
GEMINI_API_KEY=your-key genkit start -- uv run src/main.py
```
Leave the process running until the CLI prints something like:
```
Genkit Developer UI: http://localhost:4000
```
Open that URL. Port may differ if 4000 is busy.

View File

@ -0,0 +1,40 @@
# Setup — Genkit Python
## New project
**Always use a virtual environment** — never install Genkit into the system interpreter. With **uv**, the projects **`.venv`** is created and used by `uv sync` / `uv run` automatically once you add dependencies.
```bash
mkdir my-app && cd my-app
uv init
uv venv --python 3.14 .venv
# Unix: source .venv/bin/activate
# Windows: .venv\Scripts\activate
uv add genkit genkit-plugin-google-genai
export GEMINI_API_KEY=your_key_here
```
`uv init` creates `pyproject.toml`. Add your app under something like `src/main.py` (or match whatever layout `uv` generated) and point `genkit start` at that entrypoint.
## pyproject.toml
Minimal `[project]` block with unpinned Genkit deps (resolver picks compatible releases):
```toml
[project]
name = "my-app"
version = "0.1.0"
requires-python = ">=3.14"
dependencies = [
"genkit",
"genkit-plugin-google-genai",
]
```
## Plugins
Packages are **`genkit-plugin-*`** on PyPI, e.g. `genkit-plugin-google-genai`, `genkit-plugin-vertex-ai`, `genkit-plugin-anthropic`, `genkit-plugin-fastapi`. Install with `uv add genkit-plugin-<name>`.
## Python version
**3.14+**. Always use a `venv` using `uv venv --python 3.14 .venv` when creating the environment before you run any commands.

View File

@ -0,0 +1,112 @@
---
name: firebase-ai-logic
description: Official skill for integrating Firebase AI Logic (Gemini API) into web applications. Covers setup, multimodal inference, structured output, and security.
version: 1.0.0
---
# Firebase AI Logic Basics
## Overview
Firebase AI Logic is a product of Firebase that allows developers to add gen AI to their mobile and web apps using client-side SDKs. You can call Gemini models directly from your app without managing a dedicated backend. Firebase AI Logic, which was previously known as "Vertex AI for Firebase", represents the evolution of Google's AI integration platform for mobile and web developers.
It supports the two Gemini API providers:
- **Gemini Developer API**: It has a free tier ideal for prototyping, and pay-as-you-go for production
- **Vertex AI Gemini API**: Ideal for scale with enterprise-grade production readiness, requires Blaze plan
Use the Gemini Developer API as a default, and only Vertex AI Gemini API if the application requires it.
## Setup & Initialization
### Prerequisites
- Before starting, ensure you have **Node.js 16+** and npm installed. Install them if they arent already available.
- Identify the platform the user is interested in building on prior to starting: Android, iOS, Flutter or Web.
- If their platform is unsupported, Direct the user to Firebase Docs to learn how to set up AI Logic for their application (share this link with the user https://firebase.google.com/docs/ai-logic/get-started)
### Installation
The library is part of the standard Firebase Web SDK.
`npm install -g firebase@latest`
If you're in a firebase directory (with a firebase.json) the currently selected project will be marked with "current" using this command:
`npx -y firebase-tools@latest projects:list`
Ensure there's at least one app associated with the current project
`npx -y firebase-tools@latest apps:list`
Initialize AI logic SDK with the init command
`npx -y firebase-tools@latest init # Choose AI logic`
This will automatically enable the Gemini Developer API in the Firebase console.
More info in [Firebase AI Logic Getting Started](https://firebase.google.com/docs/ai-logic/get-started.md.txt)
## Core Capabilities
### Text-Only Generation
### Multimodal (Text + Images/Audio/Video/PDF input)
Firebase AI Logic allows Gemini models to analyze image files directly from your app. This enables features like creating captions, answering questions about images, detecting objects, and categorizing images. Beyond images, Gemini can analyze other media types like audio, video, and PDFs by passing them as inline data with their MIME type. For files larger than 20 megabytes (which can cause HTTP 413 errors as inline data), store them in Cloud Storage for Firebase and pass their URLs to the Gemini Developer API.
### Chat Session (Multi-turn)
Maintain history automatically using `startChat`.
### Streaming Responses
To improve the user experience by showing partial results as they arrive (like a typing effect), use `generateContentStream` instead of `generateContent` for faster display of results.
### Generate Images with Nano Banana
- Start with Gemini for most use cases, and choose Imagen for specialized tasks where image quality and specific styles are critical. (Example: gemini-2.5-flash-image)
- Requires an upgraded Blaze pay-as-you-go billing plan.
### Search Grounding with the built in googleSearch tool
## Supported Platforms and Frameworks
Supported Platforms and Frameworks include Kotlin and Java for Android, Swift for iOS, JavaScript for web apps, Dart for Flutter, and C Sharp for Unity.
## Advanced Features
### Structured Output (JSON)
Enforce a specific JSON schema for the response.
### On-Device AI (Hybrid)
Hybrid on-device inference for web apps, where the Firebase Javascript SDK automatically checks for Gemini Nano's availability (after installation) and switches between on-device or cloud-hosted prompt execution. This requires specific steps to enable model usage in the Chrome browser, more info in the [hybrid-on-device-inference documentation](https://firebase.google.com/docs/ai-logic/hybrid-on-device-inference.md.txt).
## Security & Production
### App Check
> [!WARNING]
> **Critical Safety Requirement:** In order to use AI Logic safely, you MUST set up App Check on your app. This prevents unauthorized clients from using your API quota and accessing your backend resources.
See [App Check with reCAPTCHA Enterprise](https://firebase.google.com/docs/app-check/web/recaptcha-enterprise-provider.md.txt) for setup instructions.
### Remote Config
Consider that you do not need to hardcode model names (e.g., `gemini-flash-lite-latest`). Use Firebase Remote Config to update model versions dynamically without deploying new client code. See [Changing model names remotely](https://firebase.google.com/docs/ai-logic/change-model-name-remotely.md.txt)
## Initialization Code References
| Language, Framework, Platform | Gemini API provider | Context URL |
| :---- | :---- | :---- |
| Web Modular API | Gemini Developer API (Developer API) | firebase://docs/ai-logic/get-started |
**Always use the most recent version of Gemini (gemini-flash-latest) unless another model is requested by the docs or the user. DO NOT USE gemini-1.5-flash**
## References
[Web SDK code examples and usage patterns](references/usage_patterns_web.md)

View File

@ -0,0 +1,174 @@
# Firebase AI Logic Basics
## Initialization Pattern
You must initialize the ai-logic service after the main Firebase App.
```JavaScript
import { initializeApp } from "firebase/app";
import { getAI, getGenerativeModel, GoogleAIBackend } from "firebase/ai";
// If running in Firebase App Hosting, you can skip Firebase Config and instead use:
// const app = initializeApp();
const firebaseConfig = {
// ... your firebase config
};
const app = initializeApp(firebaseConfig);
// Initialize the AI Logic service (defaults to Gemini Developer API)
// To set the AI provider, set the backend as the second parameter
const ai = getAI(firebaseApp, { backend: new GoogleAIBackend() });
const generationConfig = {
candidate_count: 1,
maxOutputTokens: 2048,
stopSequences: [],
temperature: 0.7, // Balanced: creative but focused
topP: 0.95, // Standard: allows a wide range of probable tokens
topK: 40, // Standard: considers the top 40 tokens
};
// Specify the config as part of creating the `GenerativeModel` instance
const model = getGenerativeModel(ai, { model: "gemini-2.5-flash-lite", generationConfig });
```
## Core Capabilities
Text-Only Generation
```JavaScript
async function generateText(prompt) {
const result = await model.generateContent(prompt);
const response = await result.response;
return response.text();
}
```
## Multimodal (Text + Images/Audio/Video/PDF input)
Firebase AI Logic accepts Base64 encoded data or specific file references.
```JavaScript
// Helper to convert file to base64 generic object
async function fileToGenerativePart(file) {
const base64EncodedDataPromise = new Promise((resolve) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result.split(',')[1]);
reader.readAsDataURL(file);
});
return {
inlineData: {
data: await base64EncodedDataPromise,
mimeType: file.type,
},
};
}
async function analyzeImage(prompt, imageFile) {
const imagePart = await fileToGenerativePart(imageFile);
const result = await model.generateContent([prompt, imagePart]);
return result.response.text();
}
```
## Chat Session (Multi-turn)
Maintain history automatically using startChat.
```JavaScript
const chat = model.startChat({
history: [
{
role: "user",
parts: [{ text: "Hello, I am a developer." }],
},
{
role: "model",
parts: [{ text: "Great to meet you. How can I help with code?" }],
},
],
});
async function sendMessage(msg) {
const result = await chat.sendMessage(msg);
return result.response.text();
}
```
## Streaming Responses
For real-time UI updates (like a typing effect).
```JavaScript
async function streamResponse(prompt) {
const result = await model.generateContentStream(prompt);
for await (const chunk of result.stream) {
const chunkText = chunk.text();
console.log("Stream chunk:", chunkText);
// Update UI here
}
}
```
Generate Images with Nano Banana
```Javascript
import { initializeApp } from "firebase/app";
import { getAI, getGenerativeModel, GoogleAIBackend, ResponseModality } from "firebase/ai";
// Initialize FirebaseApp
const firebaseApp = initializeApp(firebaseConfig);
// Initialize the Gemini Developer API backend service
const ai = getAI(firebaseApp, { backend: new GoogleAIBackend() });
// Create a `GenerativeModel` instance with a model that supports your use case
const model = getGenerativeModel(ai, {
model: "gemini-2.5-flash-image",
// Configure the model to respond with text and images (required)
generationConfig: {
responseModalities: [ResponseModality.TEXT, ResponseModality.IMAGE],
},
});
// Provide a text prompt instructing the model to generate an image
const prompt = 'Generate an image of the Eiffel Tower with fireworks in the background.';
// To generate an image, call `generateContent` with the text input
const result = model.generateContent(prompt);
// Handle the generated image
try {
const inlineDataParts = result.response.inlineDataParts();
if (inlineDataParts?.[0]) {
const image = inlineDataParts[0].inlineData;
console.log(image.mimeType, image.data);
}
} catch (err) {
console.error('Prompt or candidate was blocked:', err);
}
```
## Advanced Features
Structured Output (JSON)
Enforce a specific JSON schema for the response.
```JavaScript
import { getGenerativeModel, Schema } from "firebase/ai";
const jsonModel = getGenerativeModel(ai, {
model: "gemini-2.5-flash-lite",
generationConfig: {
responseMimeType: "application/json",
// Optional: Define a schema
schema = Schema.object({ ... });
}
});
async function getJsonData(prompt) {
const result = await jsonModel.generateContent(prompt);
return JSON.parse(result.response.text());
}
```
On-Device AI (Hybrid)
Automatically switch between local Gemini Nano and cloud models based on device capability.
```JavaScript
import {getGenerativeModel, InferenceMode } from "firebase/ai";
const hybridModel = getGenerativeModel(ai, { mode: InferenceMode.PREFER_ON_DEVICE });
```

View File

@ -0,0 +1,35 @@
manifest.json,1773193494224,aa3b82b9337a50b4ec6715aa37b8975cc7c2a396d8436ea9410fb2cc0c47e1bf
flutter.js,1775027253022,f18220d166d49cbef02491208c46f2c5e54db9739184e304fc55b199b5724407
favicon.png,1745728186331,19880e004e8357a51f6a64f5d1fdc8828a6432fe9c781f8acb6b730eced2cd13
icons/Icon-maskable-512.png,1758190847689,ea58e5c49bc4dcdc5db0a9fe2f25b2a3152f85ef9094f0d020de0aa318451a1d
icons/Icon-maskable-192.png,1758190847697,f7ac56197638aec92f195c5344d15395c2cbf8a5ae74e846c5b4156b64f05e38
icons/Icon-512.png,1745728186333,1de493563deb283e6ad70f0d5a89cfd61e37343df3b919c9862c5a41699ac5bc
icons/Icon-192.png,1745728186332,5fd961c51d8eb53baacac01f8e53743a76f66a43390a11f5df042e8c618ad64a
canvaskit/wimp.wasm,1775027252662,77abd773ba1776b6ce7b029866621aa42ec8a22dc43dd220af4896049260cb0a
canvaskit/wimp.js.symbols,1775027252629,cb643fbb407efb3d6735f5909c4ffd84bed8c3d8ce4840f72eb06471b18d8454
canvaskit/wimp.js,1775027252578,23db45b93eec24dd6db46ac5a6fafbf0063eb50c0dcd7d253f774e050ab1baa3
canvaskit/skwasm_heavy.wasm,1775027252578,acebd34ce89ba97c086405163d12ce74336e41bb7cca4e2fcd5011ecfabd945c
canvaskit/skwasm_heavy.js.symbols,1775027252529,d43798c11b7dbd26872e27d20df1c6c43637ebb95b4cc41fd19ff092ab2565bd
canvaskit/skwasm_heavy.js,1775027252479,dd3642e247a1ea4a8c519b76278011bb194471c590373ae2659f1d3cb793287c
canvaskit/skwasm.wasm,1775027252472,ad393be86fb17a251e0af39e56bcae5c26a4b9f20adbce626e0b7df796d89368
canvaskit/skwasm.js.symbols,1775027252441,91b787a0cd397c3cd089dc81519200c5bc35bfafa2996bb5ed4b1159553973f5
canvaskit/skwasm.js,1775027252386,91ea675d523ad0a19696479888658953e290f9798a43dae6b87d9ab60cc40c1a
canvaskit/canvaskit.wasm,1775027251440,67768ebd7649dfe70c348374dc34b732152d42467c2c335701958ba1be0949c8
canvaskit/canvaskit.js.symbols,1775027251336,a03cf8cdedf01be4a751225fa1bceadf8a783cb512ff87336bf76bb329fb29db
canvaskit/canvaskit.js,1775027251076,e0e67f4b85a07ff9b9861c9f49bf88860368ec96fb4115c0d4684f54596f9700
canvaskit/chromium/canvaskit.wasm,1775027252382,b3c17e127e4e8e4912409c050050f6f63a718b6f9618a7ec388948f2493174a6
canvaskit/chromium/canvaskit.js.symbols,1775027252321,17bdb78398d7be0b2662ad813c7f9387f79014aee6f1e3b7bf39dfe81e6b8b90
canvaskit/chromium/canvaskit.js,1775027251440,ed67753aa5382460fefd96876bf5dc40c5d242d56b37664abc8468a6772478df
version.json,1779683104794,06837fdb0bc519a4a874fb88563a1eb9e7e33bb39b77b28edd4c54b59b6afd7a
flutter_service_worker.js,1779683109173,c73b271847ffc78c576de35e95f6b46187eefb1d97dcb08eb9d84c45954ff310
index.html,1779683020112,78d10923677351d4ff7d6b283d85780271b1b52dffdc21026d43c84af1baff07
assets/FontManifest.json,1779683105459,e38b95988f5d060cf9b7ce97cb5ac9236d6f4cc04a11d69567df97b2b4cbc5e5
flutter_bootstrap.js,1779683020047,b6939c341d98fca0d0afb8303548dce06ab1b7c07c370bdedc335f28609367f6
assets/AssetManifest.bin.json,1779683105459,6763aaa55146ec9534c671aaf5a6db93265eaaa8082767ea02eb2ac9b09cbdaa
assets/AssetManifest.bin,1779683105459,93e425eb6f3c87a588cd708f748fc5bb80fa28bf34c6d814b2bd76daa6a6ed27
assets/shaders/stretch_effect.frag,1779683105794,df37894a7ac7854986bfa5d515e9af03d9138bb77be4d027db848785ebe9e357
assets/shaders/ink_sparkle.frag,1779683105735,9d529f4d93e5dabf338537416221237a0042f9cee61c98a357340d0273c67293
assets/packages/cupertino_icons/assets/CupertinoIcons.ttf,1779683108810,d41473de1f7708a0702d7f19327693486512db442f6ab0cf7774e6d6576f9fcb
assets/fonts/MaterialIcons-Regular.otf,1779683108858,b5df634ac5dd96e6ed66c12aad88379f5595dd8a998855c9a3720b04fb6a1338
assets/NOTICES,1779683105460,3f2e3b6c157a7904e4d071bc731a96781c7bb4935535646da50e7ec5c18cbc4e
main.dart.js,1779683072757,5e749727719555fe5955ccc68e603e70fc6f7193dab9fc48758a7f40e452cfe9

View File

@ -0,0 +1 @@
index.html,1777260485174,8f146c33f8eb815f8f6cfc64c024b9fd58014ce84b6f88633095aa7f1c7f3735

View File

@ -0,0 +1,5 @@
{
"projects": {
"default": "monitoring-20780"
}
}

45
Source-Code-Web/.gitignore vendored Normal file
View File

@ -0,0 +1,45 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
/coverage/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

45
Source-Code-Web/.metadata Normal file
View File

@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "d693b4b9dbac2acd4477aea4555ca6dcbea44ba2"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
- platform: android
create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
- platform: ios
create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
- platform: linux
create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
- platform: macos
create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
- platform: web
create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
- platform: windows
create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

16
Source-Code-Web/README.md Normal file
View File

@ -0,0 +1,16 @@
# smartac
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

14
Source-Code-Web/android/.gitignore vendored Normal file
View File

@ -0,0 +1,14 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks

View File

@ -0,0 +1,44 @@
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.example.smartac"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.toString()
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.smartac"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
flutter {
source = "../.."
}

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -0,0 +1,45 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="smartac"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>

View File

@ -0,0 +1,5 @@
package com.example.smartac
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -0,0 +1,24 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory =
rootProject.layout.buildDirectory
.dir("../../build")
.get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}

View File

@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip

View File

@ -0,0 +1,26 @@
pluginManagement {
val flutterSdkPath =
run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.9.1" apply false
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
}
include(":app")

View File

@ -0,0 +1,16 @@
{
"hosting": {
"public": "build/web",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}

View File

34
Source-Code-Web/ios/.gitignore vendored Normal file
View File

@ -0,0 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3

View File

@ -0,0 +1,26 @@
<?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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
</dict>
</plist>

View File

@ -0,0 +1 @@
#include "Generated.xcconfig"

View File

@ -0,0 +1 @@
#include "Generated.xcconfig"

View File

@ -0,0 +1,616 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
);
buildRules = (
);
dependencies = (
331C8086294A63A400263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C807F294A63A400263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
331C807D294A63A400263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.smartac;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.smartac.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.smartac.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.example.smartac.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.smartac;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.smartac;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -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>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@ -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>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -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>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,13 @@
import Flutter
import UIKit
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}

View File

@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

View File

@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

Some files were not shown because too many files have changed in this diff Show More