Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Labeler configuration
# Refer to https://github.com/actions/labeler for documentation

rust:
- changed-files:
- any-glob-to-any-file: ['Cargo.toml', 'Cargo.lock', 'src/**/*.rs', 'proto/**/*.proto']

python:
- changed-files:
- any-glob-to-any-file: ['python/**/*.py', 'requirements.txt']

documentation:
- changed-files:
- any-glob-to-any-file: ['*.md', 'locales/**/*.md', 'docs/**/*.md']

enhancement:
- changed-files:
- any-glob-to-any-file: ['src/**/*', 'proto/**/*']

chore:
- changed-files:
- any-glob-to-any-file: ['.github/**/*', '.gitignore', '.pre-commit-config.yaml']
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ jobs:
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- name: Install protoc
if: steps.guard.outputs.skip != 'true'
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Check formatting
if: steps.guard.outputs.skip != 'true'
run: cargo fmt --all --check
Expand All @@ -66,6 +69,9 @@ jobs:
- name: Setup Rust
if: steps.guard.outputs.skip != 'true'
uses: dtolnay/rust-toolchain@stable
- name: Install protoc
if: steps.guard.outputs.skip != 'true'
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Cache cargo
if: steps.guard.outputs.skip != 'true'
uses: actions/cache@v4
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ jobs:
permissions:
contents: read
security-events: write
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Skip if no Cargo.toml
Expand All @@ -63,6 +64,9 @@ jobs:
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Install protoc
if: steps.guard.outputs.skip != 'true'
run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
- name: Install SARIF tools
if: steps.guard.outputs.skip != 'true'
run: cargo install clippy-sarif sarif-fmt
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ Cargo.lock
__pycache__/
*.py[cod]
*$py.class
.superpowers/
*.log
docs/
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ This file is the operational core for Claude. Gemini CLI and Claude MUST follow
- Trigger stack-specific formatting (e.g., `cargo fmt`).
- Run `pre-commit run --all-files` if available.

## 🌐 Ecosystem Interaction Protocol
1. **Multi-Repo Boundaries:** You MUST NOT directly modify code in other `vtuber-*` repositories (especially `vtuber-contracts`).
2. **Issue-Based Communication:** When a change or resource is needed from another repository, you MUST:
- Draft the requirements locally in `docs/specs/ecosystem/`.
- Create a GitHub Issue in the target repository using `gh issue create`.
- Reference the Issue URL in your local progress reports.
3. **Dependency Sync:** Only implement features depending on external changes (like new Schemas) after the corresponding Issue is resolved and released.

## πŸ› οΈ Tooling & Standards
- **Translation:** All technical specifications are English. `locales/` MUST be kept in sync and translated for users documentation.
- **Workflow Mastery:** Use `/superpower:executing-plans` for feature work.
Expand Down
16 changes: 16 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "vtuber-image"
version = "0.1.0"
edition = "2021"

[dependencies]
tonic = "0.11"
prost = "0.12"
tokio = { version = "1.0", features = ["full"] }
tokio-stream = "0.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
anyhow = "1.0"

[build-dependencies]
tonic-build = "0.11"
8 changes: 8 additions & 0 deletions GEMINI.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ This file is the operational core. Gemini CLI MUST follow these protocols to mai
- Trigger stack-specific formatting (e.g., `cargo fmt`).
- Run `pre-commit run --all-files` if available.

## 🌐 Ecosystem Interaction Protocol
1. **Multi-Repo Boundaries:** You MUST NOT directly modify code in other `vtuber-*` repositories (especially `vtuber-contracts`).
2. **Issue-Based Communication:** When a change or resource is needed from another repository, you MUST:
- Draft the requirements locally in `docs/specs/ecosystem/`.
- Create a GitHub Issue in the target repository using `gh issue create`.
- Reference the Issue URL in your local progress reports.
3. **Dependency Sync:** Only implement features depending on external changes (like new Schemas) after the corresponding Issue is resolved and released.

## πŸ› οΈ Tooling & Standards
- **Translation:** All technical specifications are English. `locales/` MUST be kept in sync and translated for users documentation.
- **Workflow Mastery:** Use `/superpower:executing-plans` for feature work.
Expand Down
81 changes: 54 additions & 27 deletions STRUCTURE.tree
Original file line number Diff line number Diff line change
@@ -1,35 +1,62 @@
/
β”œβ”€β”€ GEMINI.md
β”œβ”€β”€ CLAUDE.md
β”œβ”€β”€ README.md
β”œβ”€β”€ STRUCTURE.tree
.
β”œβ”€β”€ ARCHITECTURE.md
β”œβ”€β”€ ROADMAP.md
β”œβ”€β”€ CONTRIBUTING.md
β”œβ”€β”€ build.rs
β”œβ”€β”€ Cargo.toml
β”œβ”€β”€ ci_fail.log
β”œβ”€β”€ CLAUDE.md
β”œβ”€β”€ CODE_OF_CONDUCT.md
β”œβ”€β”€ SECURITY.md
β”œβ”€β”€ LICENSE.md
β”œβ”€β”€ CONTRIBUTING.md
β”œβ”€β”€ DEPLOYMENT_GUIDE.md
β”œβ”€β”€ DESIGN_DECISIONS.md
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ docs
β”‚Β Β  └── superpowers
β”‚Β Β  β”œβ”€β”€ plans
β”‚Β Β  β”‚Β Β  └── 2026-04-30-vtuber-image-foundation.md
β”‚Β Β  └── specs
β”‚Β Β  └── 2026-04-30-vtuber-image-foundation-design.md
β”œβ”€β”€ FAQ.md
β”œβ”€β”€ GEMINI.md
β”œβ”€β”€ .github
β”‚Β Β  β”œβ”€β”€ ISSUE_TEMPLATE
β”‚Β Β  β”‚Β Β  β”œβ”€β”€ bug_report.yml
β”‚Β Β  β”‚Β Β  └── feature_request.yml
β”‚Β Β  β”œβ”€β”€ labeler.yml
β”‚Β Β  β”œβ”€β”€ PULL_REQUEST_TEMPLATE.md
β”‚Β Β  └── workflows
β”‚Β Β  β”œβ”€β”€ badges.yml
β”‚Β Β  β”œβ”€β”€ ci.yml
β”‚Β Β  β”œβ”€β”€ pr_automation.yml
β”‚Β Β  └── security.yml
β”œβ”€β”€ .gitignore
β”œβ”€β”€ GOVERNANCE.md
β”œβ”€β”€ SUPPORT.md
β”œβ”€β”€ TROUBLESHOOTING.md
β”œβ”€β”€ PHILOSOPHY.md
β”œβ”€β”€ LICENSE.md
β”œβ”€β”€ locales
β”‚Β Β  β”œβ”€β”€ README.ja.md
β”‚Β Β  β”œβ”€β”€ README.th.md
β”‚Β Β  └── README.zh.md
β”œβ”€β”€ MANIFESTO.md
β”œβ”€β”€ VISION.md
β”œβ”€β”€ STRATEGY.md
β”œβ”€β”€ PHILOSOPHY.md
β”œβ”€β”€ pr_auto_fail.log
β”œβ”€β”€ .pre-commit-config.yaml
β”œβ”€β”€ PRINCIPLES.md
β”œβ”€β”€ locales/
β”‚ β”œβ”€β”€ README.th.md
β”‚ β”œβ”€β”€ README.ja.md
β”‚ └── README.zh.md
└── .github/
β”œβ”€β”€ ISSUE_TEMPLATE/
β”‚ β”œβ”€β”€ bug_report.yml
β”‚ └── feature_request.yml
β”œβ”€β”€ PULL_REQUEST_TEMPLATE.md
└── workflows/
β”œβ”€β”€ ci.yml
β”œβ”€β”€ security.yml
└── pr_automation.yml
β”œβ”€β”€ proto
β”‚Β Β  └── vtuber_image
β”‚Β Β  └── v1
β”‚Β Β  └── image.proto
β”œβ”€β”€ python
β”‚Β Β  β”œβ”€β”€ comfy_client.py
β”‚Β Β  └── requirements.txt
β”œβ”€β”€ README.md
β”œβ”€β”€ ROADMAP.md
β”œβ”€β”€ security_fail.log
β”œβ”€β”€ SECURITY.md
β”œβ”€β”€ src
β”‚Β Β  └── main.rs
β”œβ”€β”€ STRATEGY.md
β”œβ”€β”€ STRUCTURE.tree
β”œβ”€β”€ SUPPORT.md
β”œβ”€β”€ TROUBLESHOOTING.md
└── VISION.md

14 directories, 46 files
4 changes: 4 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::configure().compile(&["proto/vtuber_image/v1/image.proto"], &["proto"])?;
Ok(())
}
18 changes: 18 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
version: '3'

services:
master:
image: chrislusf/seaweedfs
ports:
- 9333:9333
command: "master -ip=master"
volume:
image: chrislusf/seaweedfs
ports:
- 8080:8080
command: "volume -mserver=master:9333 -port=8080"
s3:
image: chrislusf/seaweedfs
ports:
- 8333:8333
command: "s3 -master=master:9333"
22 changes: 22 additions & 0 deletions proto/vtuber_image/v1/image.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
syntax = "proto3";
package vtuber_image.v1;

service ImageGenerator {
rpc Generate(GenerationRequest) returns (GenerationResponse);
}

message GenerationRequest {
string persona_id = 1;
PersonaOverrides overrides = 2;
}

message PersonaOverrides {
string hair_style = 1;
string eye_color = 2;
string outfit = 3;
}

message GenerationResponse {
string image_url = 1;
map<string, string> metadata = 2;
}
18 changes: 18 additions & 0 deletions python/comfy_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import requests
import json
import uuid

class ComfyClient:
def __init__(self, server_address="http://localhost:8188"):
self.server_address = server_address
self.client_id = str(uuid.uuid4())

def queue_prompt(self, prompt):
p = {"prompt": prompt, "client_id": self.client_id}
data = json.dumps(p).encode('utf-8')
response = requests.post(f"{self.server_address}/prompt", data=data)
return response.json()

if __name__ == "__main__":
client = ComfyClient()
print(f"Client initialized with ID: {client.client_id}")
2 changes: 2 additions & 0 deletions python/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
requests==2.31.0
websocket-client==1.7.0
56 changes: 56 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use tonic::{transport::Server, Request, Response, Status};
use vtuber_image::v1::image_generator_server::{ImageGenerator, ImageGeneratorServer};
use vtuber_image::v1::{GenerationRequest, GenerationResponse};

pub mod vtuber_image {
pub mod v1 {
tonic::include_proto!("vtuber_image.v1");
}
}

#[derive(Default)]
pub struct MyImageGenerator {}

#[tonic::async_trait]
impl ImageGenerator for MyImageGenerator {
async fn generate(
&self,
request: Request<GenerationRequest>,
) -> Result<Response<GenerationResponse>, Status> {
let req = request.into_inner();
println!("Received request for persona: {}", req.persona_id);

// Simple bridge to Python worker
let output = std::process::Command::new("python3")
.arg("python/comfy_client.py")
.output()
.map_err(|e| Status::internal(format!("Failed to execute python worker: {}", e)))?;

println!(
"Python output: {:?}",
String::from_utf8_lossy(&output.stdout)
);

let reply = GenerationResponse {
image_url: "http://placeholder.com/image.png".to_string(),
metadata: std::collections::HashMap::new(),
};

Ok(Response::new(reply))
}
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "[::1]:8083".parse()?;
let generator = MyImageGenerator::default();

println!("ImageGenerator server listening on {}", addr);

Server::builder()
.add_service(ImageGeneratorServer::new(generator))
.serve(addr)
.await?;

Ok(())
}
Loading