feat: Gemini AI integration — conversations, messages, image gen

- Fixed YAML parse error (unquoted colon in description broke @scalar/json-magic)
- Converted orval.config.ts → orval.config.cjs (fixes orval v8 TypeScript config loading)
- Codegen now works: zod schemas + React Query hooks regenerated with Gemini types
- Added Gemini tag, 4 path groups, 8 schemas to openapi.yaml
- lib/integrations-gemini-ai wired: tsconfig refs, api-server package.json dep
- Created routes/gemini.ts: CRUD conversations/messages + SSE chat stream + image gen
- Mounted /gemini router in routes/index.ts
This commit is contained in:
Replit Agent
2026-03-20 02:41:12 +00:00
parent cdb104e34f
commit e86dab0d65
54 changed files with 3620 additions and 28 deletions

View File

@@ -16,6 +16,8 @@ tags:
description: Pre-funded session balance mode (Mode 2 -- pay once, run many)
- name: demo
description: Free demo endpoint (rate-limited)
- name: gemini
description: Gemini AI chat and image operations
paths:
/healthz:
get:
@@ -385,6 +387,139 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/gemini/conversations:
get:
operationId: listGeminiConversations
tags: [gemini]
summary: List all conversations
responses:
"200":
description: List of conversations
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/GeminiConversation"
post:
operationId: createGeminiConversation
tags: [gemini]
summary: Create a new conversation
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/CreateGeminiConversationBody"
responses:
"201":
description: Created conversation
content:
application/json:
schema:
$ref: "#/components/schemas/GeminiConversation"
/gemini/conversations/{id}:
get:
operationId: getGeminiConversation
tags: [gemini]
summary: Get conversation with messages
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: Conversation with messages
content:
application/json:
schema:
$ref: "#/components/schemas/GeminiConversationWithMessages"
"404":
description: Not found
content:
application/json:
schema:
$ref: "#/components/schemas/GeminiError"
delete:
operationId: deleteGeminiConversation
tags: [gemini]
summary: Delete a conversation
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"204":
description: Deleted
"404":
description: Not found
content:
application/json:
schema:
$ref: "#/components/schemas/GeminiError"
/gemini/conversations/{id}/messages:
get:
operationId: listGeminiMessages
tags: [gemini]
summary: List messages in a conversation
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
"200":
description: List of messages
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/GeminiMessage"
post:
operationId: sendGeminiMessage
tags: [gemini]
summary: Send a message and receive an AI response (SSE stream)
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/SendGeminiMessageBody"
responses:
"200":
description: SSE stream of assistant response chunks
content:
text/event-stream: {}
/gemini/generate-image:
post:
operationId: generateGeminiImage
tags: [gemini]
summary: Generate an image from a text prompt
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/GenerateGeminiImageBody"
responses:
"200":
description: Generated image
content:
application/json:
schema:
$ref: "#/components/schemas/GenerateGeminiImageResponse"
components:
schemas:
HealthStatus:
@@ -663,8 +798,84 @@ components:
properties:
result:
type: string
GeminiConversation:
type: object
required: [id, title, createdAt]
properties:
id:
type: integer
title:
type: string
createdAt:
type: string
format: date-time
GeminiMessage:
type: object
required: [id, conversationId, role, content, createdAt]
properties:
id:
type: integer
conversationId:
type: integer
role:
type: string
content:
type: string
createdAt:
type: string
format: date-time
GeminiConversationWithMessages:
type: object
required: [id, title, createdAt, messages]
properties:
id:
type: integer
title:
type: string
createdAt:
type: string
format: date-time
messages:
type: array
items:
$ref: "#/components/schemas/GeminiMessage"
CreateGeminiConversationBody:
type: object
required: [title]
properties:
title:
type: string
SendGeminiMessageBody:
type: object
required: [content]
properties:
content:
type: string
model:
type: string
description: "Gemini model override (default: gemini-3-flash-preview)"
GenerateGeminiImageBody:
type: object
required: [prompt]
properties:
prompt:
type: string
GenerateGeminiImageResponse:
type: object
required: [b64_json, mimeType]
properties:
b64_json:
type: string
mimeType:
type: string
GeminiError:
type: object
required: [error]
properties:
error:
type: string
securitySchemes:
sessionMacaroon:
type: http
scheme: bearer
description: Session macaroon issued when a session activates. Pass as `Authorization: Bearer <macaroon>`.
description: "Session macaroon issued when a session activates. Pass as `Authorization: Bearer <macaroon>`."

View File

@@ -0,0 +1,66 @@
const path = require("path");
const root = path.resolve(__dirname, "..", "..");
const apiClientReactSrc = path.resolve(root, "lib", "api-client-react", "src");
const apiZodSrc = path.resolve(root, "lib", "api-zod", "src");
const titleTransformer = (config) => {
config.info ??= {};
config.info.title = "Api";
return config;
};
module.exports = {
"api-client-react": {
input: {
target: path.resolve(__dirname, "./openapi.yaml"),
override: {
transformer: titleTransformer,
},
},
output: {
workspace: apiClientReactSrc,
target: "generated",
client: "react-query",
mode: "split",
baseUrl: "/api",
clean: true,
prettier: true,
override: {
fetch: {
includeHttpResponseReturnType: false,
},
mutator: {
path: path.resolve(apiClientReactSrc, "custom-fetch.ts"),
name: "customFetch",
},
},
},
},
zod: {
input: {
target: path.resolve(__dirname, "./openapi.yaml"),
override: {
transformer: titleTransformer,
},
},
output: {
workspace: apiZodSrc,
client: "zod",
target: "generated",
schemas: { path: "generated/types", type: "typescript" },
mode: "split",
clean: true,
prettier: true,
override: {
zod: {
coerce: {
query: ["boolean", "number", "string"],
param: ["boolean", "number", "string"],
},
},
useDates: true,
},
},
},
};

View File

@@ -3,7 +3,7 @@
"version": "0.0.0",
"private": true,
"scripts": {
"codegen": "orval --config ./orval.config.ts"
"codegen": "orval --config ./orval.config.cjs"
},
"devDependencies": {
"orval": "^8.5.2"