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:
@@ -1247,8 +1247,16 @@ Max ~100 sanaa — pidempi vie konteksti-ikkunaa." style="color:#484f58;cursor:h
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Uusi agentti -nappi -->
|
||||
<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>
|
||||
<!-- Napit -->
|
||||
<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>
|
||||
|
||||
@@ -4780,11 +4788,255 @@ ${filesHtml}
|
||||
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)
|
||||
window.builderNew = builderNew;
|
||||
window.builderEdit = builderEdit;
|
||||
window.builderSave = builderSave;
|
||||
window.builderDelete = builderDelete;
|
||||
window.builderExportCrewAI = builderExportCrewAI;
|
||||
window.builderCancel = builderCancel;
|
||||
|
||||
// Ladataan agentit kun builder-tabi avataan
|
||||
|
||||
Reference in New Issue
Block a user