Export CrewAI: generoi ZIP-projektin agenttitiimistä

Export-nappi Agent Builderissa generoi kipina-crew.zip sisältäen:
- crew.py: CrewAI agentit, tehtävät ja sequential pipeline
- Dockerfile: Python 3.12 + uv
- docker-compose.yml: Ollama + healthcheck + mallilataus + crew
- pyproject.toml: crewai[tools] riippuvuus
- .env: Ollama-asetukset
- README.md: käynnistysohjeet

Käynnistys: docker compose run crew uv run python crew.py "projekti"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-08 11:26:27 +03:00
parent c7f3b0d79f
commit ece41dd622

View File

@@ -1247,8 +1247,16 @@ Max ~100 sanaa — pidempi vie konteksti-ikkunaa." style="color:#484f58;cursor:h
</div> </div>
</div> </div>
<!-- Uusi agentti -nappi --> <!-- Napit -->
<button onclick="builderNew()" id="builder-new-btn" style="margin-top:12px;background:#238636;border:1px solid #2ea043;color:#fff;padding:8px 20px;border-radius:6px;cursor:pointer;font-size:14px">+ Uusi agentti</button> <div style="display:flex;gap:10px;margin-top:12px;flex-wrap:wrap">
<button onclick="builderNew()" id="builder-new-btn" style="background:#238636;border:1px solid #2ea043;color:#fff;padding:8px 20px;border-radius:6px;cursor:pointer;font-size:14px">+ Uusi agentti</button>
<button onclick="builderExportCrewAI()" style="background:#0d1117;border:1px solid #58a6ff;color:#58a6ff;padding:8px 20px;border-radius:6px;cursor:pointer;font-size:14px" class="builder-tip" data-tip="Generoi koko agenttitiimistä CrewAI Python -projektin
ZIP-paketti sisältäen:
• crew.py — agentit, tehtävät ja pipeline
• Dockerfile + docker-compose.yml
• pyproject.toml riippuvuudet
• .env mallipohja">Export CrewAI</button>
</div>
</div> </div>
</div> </div>
@@ -4780,11 +4788,255 @@ ${filesHtml}
builderEditing = null; builderEditing = null;
} }
function builderExportCrewAI() {
if (builderAgents.length === 0) { alert('Ei agentteja — luo ensin agentteja.'); return; }
// Rooli-kuvaukset CrewAI:lle
const roleGoals = {
coder: 'Write clean, tested, production-ready code',
qa: 'Find bugs, write tests, ensure quality',
devops: 'Create deployment configs, CI/CD, Docker',
devsecops: 'Security auditing, OWASP compliance, vulnerability scanning',
architect: 'System design, API contracts, architecture decisions',
iac: 'Write infrastructure-as-code (OpenTofu/Terraform HCL)',
data: 'Database design, SQL optimization, data modeling',
manager: 'Break down tasks, coordinate team, prioritize work',
writer: 'Write documentation, READMEs, guides',
custom: 'Complete assigned tasks effectively',
};
// crew.py
const agentDefs = builderAgents.map(a => {
const varName = a.id.replace(/[^a-z0-9_]/g, '_');
const goal = roleGoals[a.role] || roleGoals.custom;
return `${varName} = Agent(
role="${a.name}",
goal="${goal}",
backstory="""${a.prompt.replace(/"/g, '\\"')}""",
llm="ollama/${a.model}",
verbose=True,
)`;
}).join('\n\n');
const taskDefs = builderAgents.map((a, i) => {
const varName = a.id.replace(/[^a-z0-9_]/g, '_');
const goal = roleGoals[a.role] || roleGoals.custom;
return `task_${varName} = Task(
description="${goal} for the project: {project_description}",
expected_output="Completed deliverable from ${a.name}",
agent=${varName},
)`;
}).join('\n\n');
const agentVars = builderAgents.map(a => a.id.replace(/[^a-z0-9_]/g, '_'));
const taskVars = builderAgents.map(a => 'task_' + a.id.replace(/[^a-z0-9_]/g, '_'));
const crewPy = `"""
Kipinä Agent Crew — generoitu Agent Builderista
https://kipina.studio/#builder
"""
from crewai import Agent, Task, Crew, Process
# ── Agentit ──
${agentDefs}
# ── Tehtävät ──
${taskDefs}
# ── Tiimi ──
crew = Crew(
agents=[${agentVars.join(', ')}],
tasks=[${taskVars.join(', ')}],
process=Process.sequential,
verbose=True,
)
if __name__ == "__main__":
import sys
project = sys.argv[1] if len(sys.argv) > 1 else "FastAPI + SQLite CRUD API"
result = crew.kickoff(inputs={"project_description": project})
print(result)
`;
// pyproject.toml
const pyproject = `[project]
name = "kipina-crew"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"crewai[tools]>=0.100.0",
]
[project.scripts]
crew = "crew:crew.kickoff"
`;
// .env
const models = [...new Set(builderAgents.map(a => a.model))];
const dotenv = `# Ollama-asetukset
OLLAMA_BASE_URL=http://ollama:11434
# Mallit jotka tarvitaan: ${models.join(', ')}
`;
// Dockerfile
const dockerfile = `FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
WORKDIR /app
COPY pyproject.toml ./
RUN uv sync --no-dev
COPY crew.py .env ./
CMD ["uv", "run", "python", "crew.py"]
`;
// docker-compose.yml
const ollamaPulls = models.map(m => `ollama pull ${m}`).join(' && ');
const compose = `services:
ollama:
image: ollama/ollama:latest
container_name: kipina-crew-ollama
ports:
- "11434:11434"
volumes:
- ollama-models:/root/.ollama
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:11434/api/version"]
interval: 5s
timeout: 3s
retries: 10
# Ladataan mallit Ollamaan ennen crewn käynnistystä
ollama-init:
image: ollama/ollama:latest
depends_on:
ollama:
condition: service_healthy
entrypoint: ["sh", "-c", "${ollamaPulls}"]
environment:
- OLLAMA_HOST=http://ollama:11434
crew:
build: .
container_name: kipina-crew
depends_on:
ollama-init:
condition: service_completed_successfully
environment:
- OLLAMA_BASE_URL=http://ollama:11434
# Projektin kuvaus argumenttina:
# docker compose run crew uv run python crew.py "FastAPI todo app"
volumes:
ollama-models:
`;
// README.md
const readme = `# Kipinä Agent Crew
Generoitu [Kipinä Agent Builderista](https://kipina.studio/#builder).
## Agentit
${builderAgents.map(a => `- **${a.name}** — ${a.role} (${a.model})`).join('\\n')}
## Käynnistys
\`\`\`bash
# Docker Compose (suositeltu) — käynnistää Ollaman + lataa mallit + ajaa crewn
docker compose run crew uv run python crew.py "FastAPI + SQLite CRUD API"
# Tai lokaalisti (vaatii Ollaman käynnissä)
uv sync
uv run python crew.py "FastAPI + SQLite CRUD API"
\`\`\`
`;
// Genero ZIP käyttäen samaa logiikkaa kuin downloadZip
const files = {
'crew.py': crewPy,
'pyproject.toml': pyproject,
'.env': dotenv,
'Dockerfile': dockerfile,
'docker-compose.yml': compose,
'README.md': readme,
};
function crc32(bytes) {
let crc = 0xFFFFFFFF;
for (let i = 0; i < bytes.length; i++) {
crc ^= bytes[i];
for (let j = 0; j < 8; j++) crc = (crc >>> 1) ^ (crc & 1 ? 0xEDB88320 : 0);
}
return (crc ^ 0xFFFFFFFF) >>> 0;
}
const entries = Object.entries(files);
const parts = [];
const centralDir = [];
let offset = 0;
for (const [name, content] of entries) {
const nameBytes = new TextEncoder().encode(name);
const contentBytes = new TextEncoder().encode(content || '');
const crc = crc32(contentBytes);
const header = new Uint8Array(30 + nameBytes.length);
const hv = new DataView(header.buffer);
hv.setUint32(0, 0x04034b50, true);
hv.setUint16(4, 20, true);
hv.setUint16(8, 0, true);
hv.setUint32(14, crc, true);
hv.setUint32(18, contentBytes.length, true);
hv.setUint32(22, contentBytes.length, true);
hv.setUint16(26, nameBytes.length, true);
header.set(nameBytes, 30);
parts.push(header, contentBytes);
const cd = new Uint8Array(46 + nameBytes.length);
const cv = new DataView(cd.buffer);
cv.setUint32(0, 0x02014b50, true);
cv.setUint16(4, 20, true);
cv.setUint16(6, 20, true);
cv.setUint16(10, 0, true);
cv.setUint32(16, crc, true);
cv.setUint32(20, contentBytes.length, true);
cv.setUint32(24, contentBytes.length, true);
cv.setUint16(28, nameBytes.length, true);
cv.setUint32(42, offset, true);
cd.set(nameBytes, 46);
centralDir.push(cd);
offset += header.length + contentBytes.length;
}
const cdOffset = offset;
let cdSize = 0;
for (const cd of centralDir) { parts.push(cd); cdSize += cd.length; }
const eocd = new Uint8Array(22);
const ev = new DataView(eocd.buffer);
ev.setUint32(0, 0x06054b50, true);
ev.setUint16(8, entries.length, true);
ev.setUint16(10, entries.length, true);
ev.setUint32(12, cdSize, true);
ev.setUint32(16, cdOffset, true);
parts.push(eocd);
const blob = new Blob(parts, { type: 'application/zip' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'kipina-crew.zip';
a.click();
URL.revokeObjectURL(url);
}
// Globaalit Builder-funktiot (onclick tarvitsee) // Globaalit Builder-funktiot (onclick tarvitsee)
window.builderNew = builderNew; window.builderNew = builderNew;
window.builderEdit = builderEdit; window.builderEdit = builderEdit;
window.builderSave = builderSave; window.builderSave = builderSave;
window.builderDelete = builderDelete; window.builderDelete = builderDelete;
window.builderExportCrewAI = builderExportCrewAI;
window.builderCancel = builderCancel; window.builderCancel = builderCancel;
// Ladataan agentit kun builder-tabi avataan // Ladataan agentit kun builder-tabi avataan