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
10 changes: 3 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ jobs:
echo "rust=${{ steps.stacks.outputs.rust }} python=${{ steps.stacks.outputs.python }} node=${{ steps.stacks.outputs.node }}"
echo "OK — repo structure verified. Stack-specific jobs below run only if their manifest exists."


rust-lint:
name: Rust Lint & Format
runs-on: ubuntu-latest
Expand Down Expand Up @@ -53,7 +52,7 @@ jobs:
- name: Clippy
if: steps.guard.outputs.skip == 'false'
run: cargo clippy --all-targets --all-features -- -D warnings

rust-build-and-test:
name: Rust Build & Test
runs-on: ubuntu-latest
Expand Down Expand Up @@ -92,7 +91,6 @@ jobs:
if: steps.guard.outputs.skip == 'false'
run: cargo test


mojo-build:
name: Mojo Build & Test
runs-on: ubuntu-latest
Expand Down Expand Up @@ -120,7 +118,6 @@ jobs:
if: steps.guard.outputs.skip == 'false'
run: |
# Mojo tests depend on Python stubs. Ensure they exist.
# Using the same pinned buf version for consistency.
curl -sSL https://github.com/bufbuild/buf/releases/download/v1.68.4/buf-Linux-x86_64 -o buf
chmod +x buf
./buf generate --template buf.gen.yaml
Expand All @@ -135,7 +132,6 @@ jobs:
export PYTHONPATH=$PYTHONPATH:$(pwd)/generated/python
pixi run test


buf-lint:
name: buf Lint
runs-on: ubuntu-latest
Expand All @@ -159,7 +155,7 @@ jobs:
- name: Lint
if: steps.guard.outputs.skip == 'false'
run: buf lint

buf-breaking:
name: buf Breaking-Change Gate
runs-on: ubuntu-latest
Expand Down Expand Up @@ -214,7 +210,7 @@ jobs:
- name: Breaking-change check
if: steps.guard.outputs.skip == 'false' && steps.baseline.outputs.skip == 'false'
run: buf breaking --against ".git#ref=${{ steps.baseline.outputs.ref }}"

buf-generate:
name: buf Generate (dry-run verify)
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

![Rust LOD](https://img.shields.io/badge/Rust_LOD-187-dea584.svg) ![Mojo LOD](https://img.shields.io/badge/Mojo_LOD-176-CC0000.svg) ![Total LOD](https://img.shields.io/badge/Total_LOD-3476-brightgreen.svg)

[![Rust](https://img.shields.io/badge/Rust-dea584?logo=rust&logoColor=white)](./) [![Mojo](https://img.shields.io/badge/Mojo-CC0000?logo=mojo&logoColor=white)](./) [![buf](https://img.shields.io/badge/buf-151C3B)](./) [![Pixi](https://img.shields.io/badge/Pixi-F4A02D)](./)
[![Rust](https://img.shields.io/badge/Rust-dea584?logo=rust&logoColor=white)](https://www.rust-lang.org/) [![Mojo](https://img.shields.io/badge/Mojo-CC0000?logo=mojo&logoColor=white)](https://www.modular.com/mojo) [![buf](https://img.shields.io/badge/buf-151C3B)](https://buf.build/) [![Pixi](https://img.shields.io/badge/Pixi-F4A02D)](https://pixi.sh/)

</div>

Expand Down
31 changes: 31 additions & 0 deletions docs/specs/ecosystem/2026-05-01-phase-1-integration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Phase 1: Ecosystem Integration & Asset Schemas

## 📝 Overview
This specification covers the initial integration of core contracts and the adoption of the Ecosystem Interaction Protocol.

## 🔗 Related Issues
- Fixes #1 (Assets JSON Schemas)
- Fixes #2 (Ecosystem Interaction Protocol)
- Closes #5 (Distributed Model for Image Generation)

## 🏗️ Architectural Changes
### 1. Centralized Asset Schemas (Issue #1)
Defined `proto/vtuber/v1/assets.proto` to serve as the source of truth for:
- Persona Identity & Personality
- Voice Profile Configurations
- Model Registry & Allowlist

### 2. Ecosystem Interaction Protocol (Issue #2)
Updated `GEMINI.md` and `CLAUDE.md` with rules for multi-repo coordination:
- No direct cross-repo modifications.
- Issue-based communication for dependency changes.
- Local spec drafting in `docs/specs/ecosystem/`.

### 3. Distributed Model for Image Generation (Issue #5)
- Handled transition of `image.proto` ownership.
- Removed local copy in `vtuber-contracts` to avoid drift, allowing `vtuber-image` to own its implementation-specific contract while keeping `vtuber-contracts` as the SDK publisher.

## ✅ Verification Results
- All proto files linted and verified via `buf`.
- Rust, Python, and TypeScript SDKs regenerated.
- Mojo round-trip tests passing with Thai Unicode support.
58 changes: 58 additions & 0 deletions generated/python/vtuber/v1/assets_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

124 changes: 124 additions & 0 deletions generated/python/vtuber/v1/assets_pb2.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
from google.protobuf import timestamp_pb2 as _timestamp_pb2
from google.protobuf import struct_pb2 as _struct_pb2
from google.protobuf.internal import containers as _containers
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from collections.abc import Iterable as _Iterable, Mapping as _Mapping
from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union

DESCRIPTOR: _descriptor.FileDescriptor

class PersonaAssetSchema(_message.Message):
__slots__ = ("identity", "personality", "assets", "metadata")
class Identity(_message.Message):
__slots__ = ("id", "name", "version", "description")
ID_FIELD_NUMBER: _ClassVar[int]
NAME_FIELD_NUMBER: _ClassVar[int]
VERSION_FIELD_NUMBER: _ClassVar[int]
DESCRIPTION_FIELD_NUMBER: _ClassVar[int]
id: str
name: str
version: str
description: str
def __init__(self, id: _Optional[str] = ..., name: _Optional[str] = ..., version: _Optional[str] = ..., description: _Optional[str] = ...) -> None: ...
class Personality(_message.Message):
__slots__ = ("traits", "language", "tone")
TRAITS_FIELD_NUMBER: _ClassVar[int]
LANGUAGE_FIELD_NUMBER: _ClassVar[int]
TONE_FIELD_NUMBER: _ClassVar[int]
traits: _containers.RepeatedScalarFieldContainer[str]
language: str
tone: str
def __init__(self, traits: _Optional[_Iterable[str]] = ..., language: _Optional[str] = ..., tone: _Optional[str] = ...) -> None: ...
class Assets(_message.Message):
__slots__ = ("voice_profile_id", "avatar_id")
VOICE_PROFILE_ID_FIELD_NUMBER: _ClassVar[int]
AVATAR_ID_FIELD_NUMBER: _ClassVar[int]
voice_profile_id: str
avatar_id: str
def __init__(self, voice_profile_id: _Optional[str] = ..., avatar_id: _Optional[str] = ...) -> None: ...
class Metadata(_message.Message):
__slots__ = ("created_at", "updated_at", "tags")
CREATED_AT_FIELD_NUMBER: _ClassVar[int]
UPDATED_AT_FIELD_NUMBER: _ClassVar[int]
TAGS_FIELD_NUMBER: _ClassVar[int]
created_at: _timestamp_pb2.Timestamp
updated_at: _timestamp_pb2.Timestamp
tags: _containers.RepeatedScalarFieldContainer[str]
def __init__(self, created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., updated_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., tags: _Optional[_Iterable[str]] = ...) -> None: ...
IDENTITY_FIELD_NUMBER: _ClassVar[int]
PERSONALITY_FIELD_NUMBER: _ClassVar[int]
ASSETS_FIELD_NUMBER: _ClassVar[int]
METADATA_FIELD_NUMBER: _ClassVar[int]
identity: PersonaAssetSchema.Identity
personality: PersonaAssetSchema.Personality
assets: PersonaAssetSchema.Assets
metadata: PersonaAssetSchema.Metadata
def __init__(self, identity: _Optional[_Union[PersonaAssetSchema.Identity, _Mapping]] = ..., personality: _Optional[_Union[PersonaAssetSchema.Personality, _Mapping]] = ..., assets: _Optional[_Union[PersonaAssetSchema.Assets, _Mapping]] = ..., metadata: _Optional[_Union[PersonaAssetSchema.Metadata, _Mapping]] = ...) -> None: ...

class VoiceProfileSchema(_message.Message):
__slots__ = ("id", "name", "provider", "voice_id", "settings", "metadata")
class Provider(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
PROVIDER_UNSPECIFIED: _ClassVar[VoiceProfileSchema.Provider]
PROVIDER_ELEVENLABS: _ClassVar[VoiceProfileSchema.Provider]
PROVIDER_AZURE: _ClassVar[VoiceProfileSchema.Provider]
PROVIDER_OPENAI: _ClassVar[VoiceProfileSchema.Provider]
PROVIDER_COQUI: _ClassVar[VoiceProfileSchema.Provider]
PROVIDER_UNSPECIFIED: VoiceProfileSchema.Provider
PROVIDER_ELEVENLABS: VoiceProfileSchema.Provider
PROVIDER_AZURE: VoiceProfileSchema.Provider
PROVIDER_OPENAI: VoiceProfileSchema.Provider
PROVIDER_COQUI: VoiceProfileSchema.Provider
ID_FIELD_NUMBER: _ClassVar[int]
NAME_FIELD_NUMBER: _ClassVar[int]
PROVIDER_FIELD_NUMBER: _ClassVar[int]
VOICE_ID_FIELD_NUMBER: _ClassVar[int]
SETTINGS_FIELD_NUMBER: _ClassVar[int]
METADATA_FIELD_NUMBER: _ClassVar[int]
id: str
name: str
provider: VoiceProfileSchema.Provider
voice_id: str
settings: _struct_pb2.Struct
metadata: _struct_pb2.Struct
def __init__(self, id: _Optional[str] = ..., name: _Optional[str] = ..., provider: _Optional[_Union[VoiceProfileSchema.Provider, str]] = ..., voice_id: _Optional[str] = ..., settings: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ..., metadata: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ...) -> None: ...

class ModelRegistrySchema(_message.Message):
__slots__ = ("llm_models", "allowed_models")
class LLMModel(_message.Message):
__slots__ = ("id", "provider", "max_tokens", "temperature")
ID_FIELD_NUMBER: _ClassVar[int]
PROVIDER_FIELD_NUMBER: _ClassVar[int]
MAX_TOKENS_FIELD_NUMBER: _ClassVar[int]
TEMPERATURE_FIELD_NUMBER: _ClassVar[int]
id: str
provider: str
max_tokens: int
temperature: float
def __init__(self, id: _Optional[str] = ..., provider: _Optional[str] = ..., max_tokens: _Optional[int] = ..., temperature: _Optional[float] = ...) -> None: ...
class AllowedModel(_message.Message):
__slots__ = ("id", "type", "version")
class Type(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
__slots__ = ()
TYPE_UNSPECIFIED: _ClassVar[ModelRegistrySchema.AllowedModel.Type]
TYPE_CHECKPOINT: _ClassVar[ModelRegistrySchema.AllowedModel.Type]
TYPE_LORA: _ClassVar[ModelRegistrySchema.AllowedModel.Type]
TYPE_EMBEDDING: _ClassVar[ModelRegistrySchema.AllowedModel.Type]
TYPE_UNSPECIFIED: ModelRegistrySchema.AllowedModel.Type
TYPE_CHECKPOINT: ModelRegistrySchema.AllowedModel.Type
TYPE_LORA: ModelRegistrySchema.AllowedModel.Type
TYPE_EMBEDDING: ModelRegistrySchema.AllowedModel.Type
ID_FIELD_NUMBER: _ClassVar[int]
TYPE_FIELD_NUMBER: _ClassVar[int]
VERSION_FIELD_NUMBER: _ClassVar[int]
id: str
type: ModelRegistrySchema.AllowedModel.Type
version: str
def __init__(self, id: _Optional[str] = ..., type: _Optional[_Union[ModelRegistrySchema.AllowedModel.Type, str]] = ..., version: _Optional[str] = ...) -> None: ...
LLM_MODELS_FIELD_NUMBER: _ClassVar[int]
ALLOWED_MODELS_FIELD_NUMBER: _ClassVar[int]
llm_models: _containers.RepeatedCompositeFieldContainer[ModelRegistrySchema.LLMModel]
allowed_models: _containers.RepeatedCompositeFieldContainer[ModelRegistrySchema.AllowedModel]
def __init__(self, llm_models: _Optional[_Iterable[_Union[ModelRegistrySchema.LLMModel, _Mapping]]] = ..., allowed_models: _Optional[_Iterable[_Union[ModelRegistrySchema.AllowedModel, _Mapping]]] = ...) -> None: ...
Loading
Loading