Spaces:
Runtime error
Runtime error
Upload 178 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .dockerignore +76 -0
- .env.example +44 -0
- .gitattributes +3 -0
- AI Code Block Generation_.docx +3 -0
- DEPLOYMENT_README.md +403 -0
- Dockerfile +51 -0
- Dockerfile.dev +31 -0
- FRONTEND_README.md +160 -0
- Makefile +171 -0
- README.md +91 -12
- REDIS_SETUP.md +237 -0
- Refine Scratch Block Builders_.docx +3 -0
- app.py +395 -0
- blocks/blocks.json +2221 -0
- blocks/boolean_blocks.json +281 -0
- blocks/c_blocks.json +105 -0
- blocks/cap_blocks.json +53 -0
- blocks/classwise_blocks/control_block.json +168 -0
- blocks/classwise_blocks/data_block.json +328 -0
- blocks/classwise_blocks/event_block.json +136 -0
- blocks/classwise_blocks/look_block.json +365 -0
- blocks/classwise_blocks/motion_block.json +370 -0
- blocks/classwise_blocks/operator_block.json +409 -0
- blocks/classwise_blocks/sensing_block.json +292 -0
- blocks/classwise_blocks/sound_block.json +167 -0
- blocks/hat_blocks.json +217 -0
- blocks/reporter_blocks.json +709 -0
- blocks/stack_blocks.json +1321 -0
- chat_agent/__init__.py +1 -0
- chat_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- chat_agent/api/README.md +898 -0
- chat_agent/api/__init__.py +12 -0
- chat_agent/api/__pycache__/__init__.cpython-312.pyc +0 -0
- chat_agent/api/__pycache__/chat_routes.cpython-312.pyc +0 -0
- chat_agent/api/__pycache__/health.cpython-312.pyc +0 -0
- chat_agent/api/__pycache__/middleware.cpython-312.pyc +0 -0
- chat_agent/api/__pycache__/performance_routes.cpython-312.pyc +0 -0
- chat_agent/api/chat_routes.py +642 -0
- chat_agent/api/health.py +243 -0
- chat_agent/api/middleware.py +402 -0
- chat_agent/api/performance_routes.py +441 -0
- chat_agent/models/__init__.py +15 -0
- chat_agent/models/__pycache__/__init__.cpython-312.pyc +0 -0
- chat_agent/models/__pycache__/base.cpython-312.pyc +0 -0
- chat_agent/models/__pycache__/chat_session.cpython-312.pyc +0 -0
- chat_agent/models/__pycache__/language_context.cpython-312.pyc +0 -0
- chat_agent/models/__pycache__/message.cpython-312.pyc +0 -0
- chat_agent/models/base.py +61 -0
- chat_agent/models/chat_session.py +118 -0
- chat_agent/models/language_context.py +304 -0
.dockerignore
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Git
|
| 2 |
+
.git
|
| 3 |
+
.gitignore
|
| 4 |
+
|
| 5 |
+
# Python
|
| 6 |
+
__pycache__
|
| 7 |
+
*.pyc
|
| 8 |
+
*.pyo
|
| 9 |
+
*.pyd
|
| 10 |
+
.Python
|
| 11 |
+
env
|
| 12 |
+
pip-log.txt
|
| 13 |
+
pip-delete-this-directory.txt
|
| 14 |
+
.tox
|
| 15 |
+
.coverage
|
| 16 |
+
.coverage.*
|
| 17 |
+
.cache
|
| 18 |
+
nosetests.xml
|
| 19 |
+
coverage.xml
|
| 20 |
+
*.cover
|
| 21 |
+
*.log
|
| 22 |
+
.git
|
| 23 |
+
.mypy_cache
|
| 24 |
+
.pytest_cache
|
| 25 |
+
.hypothesis
|
| 26 |
+
|
| 27 |
+
# Virtual environments
|
| 28 |
+
venv/
|
| 29 |
+
env/
|
| 30 |
+
ENV/
|
| 31 |
+
|
| 32 |
+
# IDE
|
| 33 |
+
.vscode/
|
| 34 |
+
.idea/
|
| 35 |
+
*.swp
|
| 36 |
+
*.swo
|
| 37 |
+
*~
|
| 38 |
+
|
| 39 |
+
# OS
|
| 40 |
+
.DS_Store
|
| 41 |
+
.DS_Store?
|
| 42 |
+
._*
|
| 43 |
+
.Spotlight-V100
|
| 44 |
+
.Trashes
|
| 45 |
+
ehthumbs.db
|
| 46 |
+
Thumbs.db
|
| 47 |
+
|
| 48 |
+
# Project specific
|
| 49 |
+
logs/
|
| 50 |
+
*.log
|
| 51 |
+
instance/
|
| 52 |
+
flask_session/
|
| 53 |
+
backups/
|
| 54 |
+
ssl/
|
| 55 |
+
|
| 56 |
+
# Documentation
|
| 57 |
+
docs/
|
| 58 |
+
*.md
|
| 59 |
+
!README.md
|
| 60 |
+
|
| 61 |
+
# Development files
|
| 62 |
+
.env.example
|
| 63 |
+
config/development.env
|
| 64 |
+
config/testing.env
|
| 65 |
+
docker-compose.dev.yml
|
| 66 |
+
Dockerfile.dev
|
| 67 |
+
|
| 68 |
+
# Test files
|
| 69 |
+
tests/
|
| 70 |
+
pytest.ini
|
| 71 |
+
.coverage
|
| 72 |
+
|
| 73 |
+
# Temporary files
|
| 74 |
+
tmp/
|
| 75 |
+
temp/
|
| 76 |
+
*.tmp
|
.env.example
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Groq API Configuration
|
| 2 |
+
GROQ_API_KEY=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
|
| 3 |
+
GROQ_MODEL=meta-llama/llama-4-scout-17b-16e-instruct
|
| 4 |
+
|
| 5 |
+
# Database Configuration
|
| 6 |
+
DATABASE_URL=postgresql://username:password@localhost:5432/chat_agent_db
|
| 7 |
+
DB_HOST=localhost
|
| 8 |
+
DB_PORT=5432
|
| 9 |
+
DB_NAME=chat_agent_db
|
| 10 |
+
DB_USER=admin
|
| 11 |
+
DB_PASSWORD=admin
|
| 12 |
+
|
| 13 |
+
# Redis Configuration
|
| 14 |
+
REDIS_URL=redis://localhost:6379/0
|
| 15 |
+
REDIS_HOST=localhost
|
| 16 |
+
REDIS_PORT=6379
|
| 17 |
+
REDIS_DB=0
|
| 18 |
+
REDIS_PASSWORD=Key@123
|
| 19 |
+
|
| 20 |
+
# Flask Configuration
|
| 21 |
+
FLASK_ENV=development
|
| 22 |
+
FLASK_DEBUG=True
|
| 23 |
+
SECRET_KEY=your_secret_key_here
|
| 24 |
+
|
| 25 |
+
# Session Configuration
|
| 26 |
+
SESSION_TYPE=redis
|
| 27 |
+
SESSION_PERMANENT=False
|
| 28 |
+
SESSION_USE_SIGNER=True
|
| 29 |
+
SESSION_KEY_PREFIX=chat_agent:
|
| 30 |
+
|
| 31 |
+
# Chat Agent Configuration
|
| 32 |
+
DEFAULT_LANGUAGE=python
|
| 33 |
+
MAX_CHAT_HISTORY=20
|
| 34 |
+
CONTEXT_WINDOW_SIZE=10
|
| 35 |
+
SESSION_TIMEOUT=3600
|
| 36 |
+
|
| 37 |
+
# API Configuration
|
| 38 |
+
MAX_TOKENS=2048
|
| 39 |
+
TEMPERATURE=0.7
|
| 40 |
+
STREAM_RESPONSES=True
|
| 41 |
+
|
| 42 |
+
# Logging Configuration
|
| 43 |
+
LOG_LEVEL=INFO
|
| 44 |
+
LOG_FILE=chat_agent.log
|
.gitattributes
CHANGED
|
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
AI[[:space:]]Code[[:space:]]Block[[:space:]]Generation_.docx filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
instance/chat_agent.db filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
Refine[[:space:]]Scratch[[:space:]]Block[[:space:]]Builders_.docx filter=lfs diff=lfs merge=lfs -text
|
AI Code Block Generation_.docx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bc9d14e1a3ef23a5581cf3a000d6d0275f5a85778bf10b08f90891da3997b467
|
| 3 |
+
size 6226103
|
DEPLOYMENT_README.md
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Multi-Language Chat Agent - Deployment Setup
|
| 2 |
+
|
| 3 |
+
This document provides a comprehensive overview of the deployment configuration and setup for the Multi-Language Chat Agent application.
|
| 4 |
+
|
| 5 |
+
## 🚀 Quick Start
|
| 6 |
+
|
| 7 |
+
### Docker Deployment (Recommended)
|
| 8 |
+
|
| 9 |
+
```bash
|
| 10 |
+
# Clone repository
|
| 11 |
+
git clone <repository-url>
|
| 12 |
+
cd chat-agent
|
| 13 |
+
|
| 14 |
+
# Set up environment
|
| 15 |
+
cp config/production.env .env
|
| 16 |
+
# Edit .env with your actual values
|
| 17 |
+
|
| 18 |
+
# Start with Docker Compose
|
| 19 |
+
docker-compose up -d
|
| 20 |
+
|
| 21 |
+
# Check health
|
| 22 |
+
curl http://localhost:5000/health/
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
### Manual Deployment
|
| 26 |
+
|
| 27 |
+
```bash
|
| 28 |
+
# Set up environment
|
| 29 |
+
python scripts/setup_environment.py --environment production
|
| 30 |
+
|
| 31 |
+
# Start application
|
| 32 |
+
make run-prod
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
## 📁 Configuration Files Overview
|
| 36 |
+
|
| 37 |
+
### Docker Configuration
|
| 38 |
+
|
| 39 |
+
| File | Purpose | Environment |
|
| 40 |
+
|------|---------|-------------|
|
| 41 |
+
| `Dockerfile` | Production container build | Production |
|
| 42 |
+
| `Dockerfile.dev` | Development container with hot reload | Development |
|
| 43 |
+
| `docker-compose.yml` | Production services orchestration | Production |
|
| 44 |
+
| `docker-compose.dev.yml` | Development services with volumes | Development |
|
| 45 |
+
| `nginx.conf` | Reverse proxy configuration | Production |
|
| 46 |
+
| `.dockerignore` | Docker build optimization | All |
|
| 47 |
+
|
| 48 |
+
### Environment Configuration
|
| 49 |
+
|
| 50 |
+
| File | Purpose | Usage |
|
| 51 |
+
|------|---------|-------|
|
| 52 |
+
| `config/development.env` | Development environment variables | Development |
|
| 53 |
+
| `config/production.env` | Production environment template | Production |
|
| 54 |
+
| `config/testing.env` | Testing environment configuration | Testing |
|
| 55 |
+
| `.env.example` | Environment template with examples | Reference |
|
| 56 |
+
|
| 57 |
+
### Database Configuration
|
| 58 |
+
|
| 59 |
+
| File | Purpose | Usage |
|
| 60 |
+
|------|---------|-------|
|
| 61 |
+
| `migrations/001_initial_schema.sql` | Database schema definition | All environments |
|
| 62 |
+
| `migrations/migrate.py` | Migration management script | All environments |
|
| 63 |
+
| `scripts/init_db.py` | Database initialization utility | Setup |
|
| 64 |
+
|
| 65 |
+
### Deployment Scripts
|
| 66 |
+
|
| 67 |
+
| File | Purpose | Usage |
|
| 68 |
+
|------|---------|-------|
|
| 69 |
+
| `scripts/setup_environment.py` | Automated environment setup | Setup |
|
| 70 |
+
| `scripts/init_db.py` | Database management | Setup/Maintenance |
|
| 71 |
+
| `Makefile` | Common development tasks | Development |
|
| 72 |
+
|
| 73 |
+
## 🏗️ Architecture Overview
|
| 74 |
+
|
| 75 |
+
```
|
| 76 |
+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
| 77 |
+
│ Nginx │ │ Chat Agent │ │ PostgreSQL │
|
| 78 |
+
│ (Reverse Proxy)│────│ Application │────│ Database │
|
| 79 |
+
│ │ │ │ │ │
|
| 80 |
+
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
| 81 |
+
│
|
| 82 |
+
│
|
| 83 |
+
┌─────────────────┐
|
| 84 |
+
│ Redis │
|
| 85 |
+
│ (Cache/ │
|
| 86 |
+
│ Sessions) │
|
| 87 |
+
└─────────────────┘
|
| 88 |
+
```
|
| 89 |
+
|
| 90 |
+
## 🔧 Configuration Management
|
| 91 |
+
|
| 92 |
+
### Environment-Specific Settings
|
| 93 |
+
|
| 94 |
+
The application supports three environments with different configurations:
|
| 95 |
+
|
| 96 |
+
#### Development
|
| 97 |
+
- Debug mode enabled
|
| 98 |
+
- Detailed logging
|
| 99 |
+
- Local database
|
| 100 |
+
- Hot reload
|
| 101 |
+
- Development tools
|
| 102 |
+
|
| 103 |
+
#### Production
|
| 104 |
+
- Optimized for performance
|
| 105 |
+
- Security hardened
|
| 106 |
+
- Production database
|
| 107 |
+
- Monitoring enabled
|
| 108 |
+
- Error handling
|
| 109 |
+
|
| 110 |
+
#### Testing
|
| 111 |
+
- In-memory database
|
| 112 |
+
- Isolated test data
|
| 113 |
+
- Minimal logging
|
| 114 |
+
- Fast execution
|
| 115 |
+
|
| 116 |
+
### Configuration Hierarchy
|
| 117 |
+
|
| 118 |
+
1. **Environment Variables** (highest priority)
|
| 119 |
+
2. **`.env` file**
|
| 120 |
+
3. **Config class defaults**
|
| 121 |
+
4. **Application defaults** (lowest priority)
|
| 122 |
+
|
| 123 |
+
## 🐳 Docker Deployment
|
| 124 |
+
|
| 125 |
+
### Production Deployment
|
| 126 |
+
|
| 127 |
+
```bash
|
| 128 |
+
# Build and start all services
|
| 129 |
+
docker-compose up -d
|
| 130 |
+
|
| 131 |
+
# With nginx reverse proxy
|
| 132 |
+
docker-compose --profile production up -d
|
| 133 |
+
|
| 134 |
+
# Scale application instances
|
| 135 |
+
docker-compose up -d --scale chat-agent=3
|
| 136 |
+
```
|
| 137 |
+
|
| 138 |
+
### Development Deployment
|
| 139 |
+
|
| 140 |
+
```bash
|
| 141 |
+
# Start development environment
|
| 142 |
+
docker-compose -f docker-compose.dev.yml up -d
|
| 143 |
+
|
| 144 |
+
# View logs
|
| 145 |
+
docker-compose -f docker-compose.dev.yml logs -f
|
| 146 |
+
```
|
| 147 |
+
|
| 148 |
+
### Docker Services
|
| 149 |
+
|
| 150 |
+
| Service | Purpose | Ports | Dependencies |
|
| 151 |
+
|---------|---------|-------|--------------|
|
| 152 |
+
| `chat-agent` | Main application | 5000 | postgres, redis |
|
| 153 |
+
| `postgres` | Database | 5432 | - |
|
| 154 |
+
| `redis` | Cache/Sessions | 6379 | - |
|
| 155 |
+
| `nginx` | Reverse proxy | 80, 443 | chat-agent |
|
| 156 |
+
|
| 157 |
+
## 🗄️ Database Management
|
| 158 |
+
|
| 159 |
+
### Initialization
|
| 160 |
+
|
| 161 |
+
```bash
|
| 162 |
+
# Initialize database with schema
|
| 163 |
+
python scripts/init_db.py init --config production
|
| 164 |
+
|
| 165 |
+
# Check migration status
|
| 166 |
+
python scripts/init_db.py status --config production
|
| 167 |
+
|
| 168 |
+
# Seed with sample data
|
| 169 |
+
python scripts/init_db.py seed --config production
|
| 170 |
+
```
|
| 171 |
+
|
| 172 |
+
### Migrations
|
| 173 |
+
|
| 174 |
+
```bash
|
| 175 |
+
# Run migrations manually
|
| 176 |
+
python migrations/migrate.py migrate --config production
|
| 177 |
+
|
| 178 |
+
# Check migration status
|
| 179 |
+
python migrations/migrate.py status --config production
|
| 180 |
+
```
|
| 181 |
+
|
| 182 |
+
### Backup and Restore
|
| 183 |
+
|
| 184 |
+
```bash
|
| 185 |
+
# Create backup
|
| 186 |
+
make backup-db
|
| 187 |
+
|
| 188 |
+
# Restore from backup
|
| 189 |
+
make restore-db
|
| 190 |
+
```
|
| 191 |
+
|
| 192 |
+
## 🔍 Health Monitoring
|
| 193 |
+
|
| 194 |
+
### Health Check Endpoints
|
| 195 |
+
|
| 196 |
+
| Endpoint | Purpose | Use Case |
|
| 197 |
+
|----------|---------|----------|
|
| 198 |
+
| `/health/` | Basic health check | Load balancer |
|
| 199 |
+
| `/health/detailed` | Component status | Monitoring |
|
| 200 |
+
| `/health/ready` | Readiness probe | Kubernetes |
|
| 201 |
+
| `/health/live` | Liveness probe | Kubernetes |
|
| 202 |
+
| `/health/metrics` | Application metrics | Monitoring |
|
| 203 |
+
|
| 204 |
+
### Example Health Check
|
| 205 |
+
|
| 206 |
+
```bash
|
| 207 |
+
# Basic health check
|
| 208 |
+
curl http://localhost:5000/health/
|
| 209 |
+
|
| 210 |
+
# Detailed status
|
| 211 |
+
curl http://localhost:5000/health/detailed | jq
|
| 212 |
+
```
|
| 213 |
+
|
| 214 |
+
### Monitoring Integration
|
| 215 |
+
|
| 216 |
+
The health endpoints are designed to work with:
|
| 217 |
+
- **Load Balancers**: Nginx, HAProxy, AWS ALB
|
| 218 |
+
- **Container Orchestration**: Kubernetes, Docker Swarm
|
| 219 |
+
- **Monitoring Systems**: Prometheus, Grafana, DataDog
|
| 220 |
+
|
| 221 |
+
## 🔒 Security Configuration
|
| 222 |
+
|
| 223 |
+
### Production Security Checklist
|
| 224 |
+
|
| 225 |
+
- [ ] **HTTPS**: SSL/TLS certificates configured
|
| 226 |
+
- [ ] **API Keys**: Stored securely, not in code
|
| 227 |
+
- [ ] **Database**: Connection encryption enabled
|
| 228 |
+
- [ ] **Firewall**: Proper rules configured
|
| 229 |
+
- [ ] **Headers**: Security headers set in Nginx
|
| 230 |
+
- [ ] **Rate Limiting**: Configured for API endpoints
|
| 231 |
+
- [ ] **Input Validation**: All inputs sanitized
|
| 232 |
+
- [ ] **Logging**: Security events logged
|
| 233 |
+
|
| 234 |
+
### Environment Variables Security
|
| 235 |
+
|
| 236 |
+
```bash
|
| 237 |
+
# Use secure secret generation
|
| 238 |
+
python -c "import secrets; print(secrets.token_urlsafe(32))"
|
| 239 |
+
|
| 240 |
+
# Store in secure location
|
| 241 |
+
export SECRET_KEY="your_secure_key"
|
| 242 |
+
export GROQ_API_KEY="your_api_key"
|
| 243 |
+
```
|
| 244 |
+
|
| 245 |
+
## 📊 Performance Optimization
|
| 246 |
+
|
| 247 |
+
### Application Performance
|
| 248 |
+
|
| 249 |
+
- **Connection Pooling**: Database and Redis connections
|
| 250 |
+
- **Caching**: Redis for sessions and frequent data
|
| 251 |
+
- **Async Processing**: WebSocket for real-time communication
|
| 252 |
+
- **Load Balancing**: Multiple application instances
|
| 253 |
+
|
| 254 |
+
### Database Performance
|
| 255 |
+
|
| 256 |
+
```sql
|
| 257 |
+
-- Recommended indexes
|
| 258 |
+
CREATE INDEX CONCURRENTLY idx_messages_session_timestamp
|
| 259 |
+
ON messages(session_id, timestamp);
|
| 260 |
+
|
| 261 |
+
CREATE INDEX CONCURRENTLY idx_sessions_user_active
|
| 262 |
+
ON chat_sessions(user_id, is_active);
|
| 263 |
+
```
|
| 264 |
+
|
| 265 |
+
### Redis Configuration
|
| 266 |
+
|
| 267 |
+
```bash
|
| 268 |
+
# Production Redis settings
|
| 269 |
+
maxmemory 512mb
|
| 270 |
+
maxmemory-policy allkeys-lru
|
| 271 |
+
save 900 1
|
| 272 |
+
save 300 10
|
| 273 |
+
```
|
| 274 |
+
|
| 275 |
+
## 🚨 Troubleshooting
|
| 276 |
+
|
| 277 |
+
### Common Issues
|
| 278 |
+
|
| 279 |
+
#### 1. Container Won't Start
|
| 280 |
+
|
| 281 |
+
```bash
|
| 282 |
+
# Check logs
|
| 283 |
+
docker-compose logs chat-agent
|
| 284 |
+
|
| 285 |
+
# Check health
|
| 286 |
+
docker-compose exec chat-agent curl localhost:5000/health/
|
| 287 |
+
```
|
| 288 |
+
|
| 289 |
+
#### 2. Database Connection Failed
|
| 290 |
+
|
| 291 |
+
```bash
|
| 292 |
+
# Test database connectivity
|
| 293 |
+
docker-compose exec postgres psql -U chatuser -d chat_agent_db -c "SELECT 1;"
|
| 294 |
+
|
| 295 |
+
# Check environment variables
|
| 296 |
+
docker-compose exec chat-agent env | grep DATABASE
|
| 297 |
+
```
|
| 298 |
+
|
| 299 |
+
#### 3. Redis Connection Issues
|
| 300 |
+
|
| 301 |
+
```bash
|
| 302 |
+
# Test Redis connectivity
|
| 303 |
+
docker-compose exec redis redis-cli ping
|
| 304 |
+
|
| 305 |
+
# Check Redis logs
|
| 306 |
+
docker-compose logs redis
|
| 307 |
+
```
|
| 308 |
+
|
| 309 |
+
### Log Analysis
|
| 310 |
+
|
| 311 |
+
```bash
|
| 312 |
+
# Application logs
|
| 313 |
+
docker-compose logs -f chat-agent
|
| 314 |
+
|
| 315 |
+
# Database logs
|
| 316 |
+
docker-compose logs -f postgres
|
| 317 |
+
|
| 318 |
+
# All services
|
| 319 |
+
docker-compose logs -f
|
| 320 |
+
```
|
| 321 |
+
|
| 322 |
+
## 📈 Scaling
|
| 323 |
+
|
| 324 |
+
### Horizontal Scaling
|
| 325 |
+
|
| 326 |
+
```bash
|
| 327 |
+
# Scale application instances
|
| 328 |
+
docker-compose up -d --scale chat-agent=3
|
| 329 |
+
|
| 330 |
+
# Update nginx upstream configuration
|
| 331 |
+
# Add multiple server entries in nginx.conf
|
| 332 |
+
```
|
| 333 |
+
|
| 334 |
+
### Load Balancing
|
| 335 |
+
|
| 336 |
+
```nginx
|
| 337 |
+
upstream chat_agent {
|
| 338 |
+
server chat-agent_1:5000;
|
| 339 |
+
server chat-agent_2:5000;
|
| 340 |
+
server chat-agent_3:5000;
|
| 341 |
+
}
|
| 342 |
+
```
|
| 343 |
+
|
| 344 |
+
### Database Scaling
|
| 345 |
+
|
| 346 |
+
- **Read Replicas**: For read-heavy workloads
|
| 347 |
+
- **Connection Pooling**: PgBouncer for connection management
|
| 348 |
+
- **Partitioning**: For large message tables
|
| 349 |
+
|
| 350 |
+
## 🔄 CI/CD Integration
|
| 351 |
+
|
| 352 |
+
### GitHub Actions Example
|
| 353 |
+
|
| 354 |
+
```yaml
|
| 355 |
+
name: Deploy to Production
|
| 356 |
+
|
| 357 |
+
on:
|
| 358 |
+
push:
|
| 359 |
+
branches: [main]
|
| 360 |
+
|
| 361 |
+
jobs:
|
| 362 |
+
deploy:
|
| 363 |
+
runs-on: ubuntu-latest
|
| 364 |
+
steps:
|
| 365 |
+
- uses: actions/checkout@v2
|
| 366 |
+
|
| 367 |
+
- name: Build and Deploy
|
| 368 |
+
run: |
|
| 369 |
+
docker-compose build
|
| 370 |
+
docker-compose up -d
|
| 371 |
+
|
| 372 |
+
- name: Health Check
|
| 373 |
+
run: |
|
| 374 |
+
sleep 30
|
| 375 |
+
curl -f http://localhost:5000/health/ || exit 1
|
| 376 |
+
```
|
| 377 |
+
|
| 378 |
+
## 📚 Additional Resources
|
| 379 |
+
|
| 380 |
+
- **[Deployment Guide](docs/DEPLOYMENT.md)**: Detailed deployment instructions
|
| 381 |
+
- **[Environment Setup](docs/ENVIRONMENT_SETUP.md)**: Manual setup guide
|
| 382 |
+
- **[API Documentation](chat_agent/api/README.md)**: API reference
|
| 383 |
+
- **[Feature Specifications](.kiro/specs/multi-language-chat-agent/)**: Requirements and design
|
| 384 |
+
|
| 385 |
+
## 🆘 Support
|
| 386 |
+
|
| 387 |
+
For deployment issues:
|
| 388 |
+
|
| 389 |
+
1. Check the [troubleshooting section](#-troubleshooting)
|
| 390 |
+
2. Review application logs
|
| 391 |
+
3. Verify configuration settings
|
| 392 |
+
4. Test health endpoints
|
| 393 |
+
5. Check system resources
|
| 394 |
+
|
| 395 |
+
## 📝 Changelog
|
| 396 |
+
|
| 397 |
+
### Version 1.0.0
|
| 398 |
+
- Initial deployment configuration
|
| 399 |
+
- Docker containerization
|
| 400 |
+
- Health monitoring
|
| 401 |
+
- Environment management
|
| 402 |
+
- Database migrations
|
| 403 |
+
- Security hardening
|
Dockerfile
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Multi-stage build for production optimization
|
| 2 |
+
FROM python:3.11-slim as builder
|
| 3 |
+
|
| 4 |
+
# Set working directory
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
|
| 7 |
+
# Install system dependencies for building Python packages
|
| 8 |
+
RUN apt-get update && apt-get install -y \
|
| 9 |
+
gcc \
|
| 10 |
+
g++ \
|
| 11 |
+
libpq-dev \
|
| 12 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 13 |
+
|
| 14 |
+
# Copy requirements and install Python dependencies
|
| 15 |
+
COPY requirements.txt .
|
| 16 |
+
RUN pip install --no-cache-dir --user -r requirements.txt
|
| 17 |
+
|
| 18 |
+
# Production stage
|
| 19 |
+
FROM python:3.11-slim
|
| 20 |
+
|
| 21 |
+
# Set working directory
|
| 22 |
+
WORKDIR /app
|
| 23 |
+
|
| 24 |
+
# Install runtime dependencies
|
| 25 |
+
RUN apt-get update && apt-get install -y \
|
| 26 |
+
libpq5 \
|
| 27 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 28 |
+
|
| 29 |
+
# Copy Python packages from builder stage
|
| 30 |
+
COPY --from=builder /root/.local /root/.local
|
| 31 |
+
|
| 32 |
+
# Make sure scripts in .local are usable
|
| 33 |
+
ENV PATH=/root/.local/bin:$PATH
|
| 34 |
+
|
| 35 |
+
# Copy application code
|
| 36 |
+
COPY . .
|
| 37 |
+
|
| 38 |
+
# Create non-root user for security
|
| 39 |
+
RUN groupadd -r chatuser && useradd -r -g chatuser chatuser
|
| 40 |
+
RUN chown -R chatuser:chatuser /app
|
| 41 |
+
USER chatuser
|
| 42 |
+
|
| 43 |
+
# Expose port
|
| 44 |
+
EXPOSE 5000
|
| 45 |
+
|
| 46 |
+
# Health check
|
| 47 |
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
| 48 |
+
CMD python -c "import requests; requests.get('http://localhost:5000/health')" || exit 1
|
| 49 |
+
|
| 50 |
+
# Default command
|
| 51 |
+
CMD ["python", "app.py"]
|
Dockerfile.dev
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Development Dockerfile with hot reload
|
| 2 |
+
FROM python:3.11-slim
|
| 3 |
+
|
| 4 |
+
# Set working directory
|
| 5 |
+
WORKDIR /app
|
| 6 |
+
|
| 7 |
+
# Install system dependencies
|
| 8 |
+
RUN apt-get update && apt-get install -y \
|
| 9 |
+
gcc \
|
| 10 |
+
g++ \
|
| 11 |
+
libpq-dev \
|
| 12 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 13 |
+
|
| 14 |
+
# Copy requirements and install Python dependencies
|
| 15 |
+
COPY requirements.txt .
|
| 16 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 17 |
+
|
| 18 |
+
# Install development dependencies
|
| 19 |
+
RUN pip install --no-cache-dir \
|
| 20 |
+
watchdog \
|
| 21 |
+
flask-debugtoolbar \
|
| 22 |
+
ipdb
|
| 23 |
+
|
| 24 |
+
# Copy application code
|
| 25 |
+
COPY . .
|
| 26 |
+
|
| 27 |
+
# Expose port
|
| 28 |
+
EXPOSE 5000
|
| 29 |
+
|
| 30 |
+
# Development command with hot reload
|
| 31 |
+
CMD ["python", "-m", "flask", "run", "--host=0.0.0.0", "--port=5000", "--reload"]
|
FRONTEND_README.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Chat Interface Frontend
|
| 2 |
+
|
| 3 |
+
This document describes the frontend chat interface implementation for the multi-language chat agent.
|
| 4 |
+
|
| 5 |
+
## Features Implemented
|
| 6 |
+
|
| 7 |
+
### ✅ Responsive Chat UI
|
| 8 |
+
- Clean, modern interface with gradient header
|
| 9 |
+
- Responsive design that works on desktop and mobile
|
| 10 |
+
- Dark mode support via CSS media queries
|
| 11 |
+
- Smooth animations and transitions
|
| 12 |
+
|
| 13 |
+
### ✅ WebSocket Client
|
| 14 |
+
- Real-time communication with backend via Socket.IO
|
| 15 |
+
- Automatic reconnection handling
|
| 16 |
+
- Connection status indicators
|
| 17 |
+
- Error handling and user feedback
|
| 18 |
+
|
| 19 |
+
### ✅ Language Selection
|
| 20 |
+
- Dropdown with 8 supported programming languages:
|
| 21 |
+
- Python (default)
|
| 22 |
+
- JavaScript
|
| 23 |
+
- Java
|
| 24 |
+
- C++
|
| 25 |
+
- C#
|
| 26 |
+
- Go
|
| 27 |
+
- Rust
|
| 28 |
+
- TypeScript
|
| 29 |
+
- Real-time language switching
|
| 30 |
+
- Visual feedback when language changes
|
| 31 |
+
|
| 32 |
+
### ✅ Syntax Highlighting
|
| 33 |
+
- Prism.js integration for code block highlighting
|
| 34 |
+
- Automatic language detection
|
| 35 |
+
- Support for inline code and code blocks
|
| 36 |
+
- Click-to-copy functionality for code blocks
|
| 37 |
+
|
| 38 |
+
### ✅ Typing Indicators
|
| 39 |
+
- Visual typing animation while assistant responds
|
| 40 |
+
- Streaming response support with real-time updates
|
| 41 |
+
- Processing status indicators
|
| 42 |
+
|
| 43 |
+
### ✅ Connection Status
|
| 44 |
+
- Visual connection status indicator (connected/disconnected/connecting)
|
| 45 |
+
- Automatic reconnection attempts
|
| 46 |
+
- Connection health monitoring
|
| 47 |
+
|
| 48 |
+
### ✅ Error Message Display
|
| 49 |
+
- User-friendly error messages
|
| 50 |
+
- Auto-dismissing error notifications
|
| 51 |
+
- Toast notifications for actions like copying code
|
| 52 |
+
- Character count with warnings
|
| 53 |
+
|
| 54 |
+
## Files Created
|
| 55 |
+
|
| 56 |
+
### HTML Template
|
| 57 |
+
- `templates/chat.html` - Main chat interface template
|
| 58 |
+
|
| 59 |
+
### CSS Styles
|
| 60 |
+
- `static/css/chat.css` - Complete responsive styling with:
|
| 61 |
+
- Modern gradient design
|
| 62 |
+
- Responsive breakpoints
|
| 63 |
+
- Dark mode support
|
| 64 |
+
- Smooth animations
|
| 65 |
+
- Code block styling
|
| 66 |
+
|
| 67 |
+
### JavaScript Client
|
| 68 |
+
- `static/js/chat.js` - WebSocket client with:
|
| 69 |
+
- ChatClient class for managing connections
|
| 70 |
+
- Real-time message handling
|
| 71 |
+
- Language switching
|
| 72 |
+
- Syntax highlighting integration
|
| 73 |
+
- Error handling and notifications
|
| 74 |
+
|
| 75 |
+
## How to Use
|
| 76 |
+
|
| 77 |
+
1. **Start the Flask Application**
|
| 78 |
+
```bash
|
| 79 |
+
python app.py
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
2. **Open Browser**
|
| 83 |
+
- Navigate to `http://localhost:5000`
|
| 84 |
+
- The chat interface will load automatically
|
| 85 |
+
|
| 86 |
+
3. **Chat Features**
|
| 87 |
+
- Type messages in the input field
|
| 88 |
+
- Press Enter to send (Shift+Enter for new lines)
|
| 89 |
+
- Select different programming languages from dropdown
|
| 90 |
+
- Click code blocks to copy them
|
| 91 |
+
- Monitor connection status in header
|
| 92 |
+
|
| 93 |
+
## Demo Mode
|
| 94 |
+
|
| 95 |
+
The current implementation includes a demo WebSocket handler that:
|
| 96 |
+
- Simulates streaming responses
|
| 97 |
+
- Handles language switching
|
| 98 |
+
- Provides helpful demo messages
|
| 99 |
+
- Shows all UI features working
|
| 100 |
+
|
| 101 |
+
## Integration with Backend
|
| 102 |
+
|
| 103 |
+
The frontend is designed to work with the full chat agent backend. Key integration points:
|
| 104 |
+
|
| 105 |
+
### WebSocket Events
|
| 106 |
+
- `connect` - Establish connection with auth
|
| 107 |
+
- `message` - Send chat messages
|
| 108 |
+
- `language_switch` - Change programming language
|
| 109 |
+
- `disconnect` - Clean disconnection
|
| 110 |
+
|
| 111 |
+
### Expected Backend Events
|
| 112 |
+
- `connection_status` - Connection confirmation
|
| 113 |
+
- `response_start` - Begin streaming response
|
| 114 |
+
- `response_chunk` - Stream response content
|
| 115 |
+
- `response_complete` - End streaming response
|
| 116 |
+
- `language_switched` - Language change confirmation
|
| 117 |
+
- `error` - Error notifications
|
| 118 |
+
|
| 119 |
+
## Browser Compatibility
|
| 120 |
+
|
| 121 |
+
- Modern browsers with WebSocket support
|
| 122 |
+
- Chrome, Firefox, Safari, Edge
|
| 123 |
+
- Mobile browsers (iOS Safari, Chrome Mobile)
|
| 124 |
+
- Fallback to polling for older browsers
|
| 125 |
+
|
| 126 |
+
## Performance Features
|
| 127 |
+
|
| 128 |
+
- Efficient DOM updates
|
| 129 |
+
- Smooth scrolling to new messages
|
| 130 |
+
- Optimized syntax highlighting
|
| 131 |
+
- Minimal memory footprint
|
| 132 |
+
- Automatic cleanup on disconnect
|
| 133 |
+
|
| 134 |
+
## Security Considerations
|
| 135 |
+
|
| 136 |
+
- Input sanitization for XSS prevention
|
| 137 |
+
- Message length limits (2000 characters)
|
| 138 |
+
- Rate limiting ready (handled by backend)
|
| 139 |
+
- Secure WebSocket connections (WSS in production)
|
| 140 |
+
|
| 141 |
+
## Next Steps
|
| 142 |
+
|
| 143 |
+
The frontend is ready for integration with:
|
| 144 |
+
1. Full chat agent backend (Task 11)
|
| 145 |
+
2. User authentication system
|
| 146 |
+
3. Session persistence
|
| 147 |
+
4. Chat history loading
|
| 148 |
+
5. File upload capabilities
|
| 149 |
+
6. Advanced code execution features
|
| 150 |
+
|
| 151 |
+
## Testing
|
| 152 |
+
|
| 153 |
+
The interface has been tested with:
|
| 154 |
+
- WebSocket connection establishment
|
| 155 |
+
- Message sending and receiving
|
| 156 |
+
- Language switching
|
| 157 |
+
- Error handling
|
| 158 |
+
- Responsive design
|
| 159 |
+
- Code copying functionality
|
| 160 |
+
- Real-time streaming responses
|
Makefile
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Makefile for Chat Agent Application
|
| 2 |
+
|
| 3 |
+
.PHONY: help install setup test clean run docker-build docker-run docker-dev migrate seed
|
| 4 |
+
|
| 5 |
+
# Default target
|
| 6 |
+
help:
|
| 7 |
+
@echo "Available commands:"
|
| 8 |
+
@echo " install - Install dependencies"
|
| 9 |
+
@echo " setup - Set up development environment"
|
| 10 |
+
@echo " setup-prod - Set up production environment"
|
| 11 |
+
@echo " test - Run tests"
|
| 12 |
+
@echo " test-cov - Run tests with coverage"
|
| 13 |
+
@echo " clean - Clean up temporary files"
|
| 14 |
+
@echo " run - Run development server"
|
| 15 |
+
@echo " run-prod - Run production server"
|
| 16 |
+
@echo " migrate - Run database migrations"
|
| 17 |
+
@echo " seed - Seed database with sample data"
|
| 18 |
+
@echo " reset-db - Reset database (development only)"
|
| 19 |
+
@echo " docker-build - Build Docker image"
|
| 20 |
+
@echo " docker-run - Run with Docker Compose"
|
| 21 |
+
@echo " docker-dev - Run development environment with Docker"
|
| 22 |
+
@echo " docker-stop - Stop Docker containers"
|
| 23 |
+
@echo " lint - Run code linting"
|
| 24 |
+
@echo " format - Format code"
|
| 25 |
+
|
| 26 |
+
# Installation and setup
|
| 27 |
+
install:
|
| 28 |
+
pip install --upgrade pip
|
| 29 |
+
pip install -r requirements.txt
|
| 30 |
+
|
| 31 |
+
setup:
|
| 32 |
+
python scripts/setup_environment.py --environment development
|
| 33 |
+
|
| 34 |
+
setup-prod:
|
| 35 |
+
python scripts/setup_environment.py --environment production
|
| 36 |
+
|
| 37 |
+
setup-test:
|
| 38 |
+
python scripts/setup_environment.py --environment testing
|
| 39 |
+
|
| 40 |
+
# Testing
|
| 41 |
+
test:
|
| 42 |
+
python -m pytest tests/ -v
|
| 43 |
+
|
| 44 |
+
test-cov:
|
| 45 |
+
python -m pytest tests/ --cov=chat_agent --cov-report=html --cov-report=term
|
| 46 |
+
|
| 47 |
+
test-integration:
|
| 48 |
+
python -m pytest tests/integration/ -v
|
| 49 |
+
|
| 50 |
+
test-unit:
|
| 51 |
+
python -m pytest tests/unit/ -v
|
| 52 |
+
|
| 53 |
+
# Database operations
|
| 54 |
+
migrate:
|
| 55 |
+
python scripts/init_db.py init --config development
|
| 56 |
+
|
| 57 |
+
migrate-prod:
|
| 58 |
+
python scripts/init_db.py init --config production
|
| 59 |
+
|
| 60 |
+
seed:
|
| 61 |
+
python scripts/init_db.py seed --config development
|
| 62 |
+
|
| 63 |
+
reset-db:
|
| 64 |
+
python scripts/init_db.py reset --config development
|
| 65 |
+
|
| 66 |
+
db-status:
|
| 67 |
+
python scripts/init_db.py status --config development
|
| 68 |
+
|
| 69 |
+
# Application running
|
| 70 |
+
run:
|
| 71 |
+
python app.py
|
| 72 |
+
|
| 73 |
+
run-prod:
|
| 74 |
+
gunicorn --bind 0.0.0.0:5000 --workers 4 --worker-class eventlet app:app
|
| 75 |
+
|
| 76 |
+
run-debug:
|
| 77 |
+
FLASK_DEBUG=True python app.py
|
| 78 |
+
|
| 79 |
+
# Docker operations
|
| 80 |
+
docker-build:
|
| 81 |
+
docker build -t chat-agent .
|
| 82 |
+
|
| 83 |
+
docker-run:
|
| 84 |
+
docker-compose up -d
|
| 85 |
+
|
| 86 |
+
docker-dev:
|
| 87 |
+
docker-compose -f docker-compose.dev.yml up -d
|
| 88 |
+
|
| 89 |
+
docker-stop:
|
| 90 |
+
docker-compose down
|
| 91 |
+
docker-compose -f docker-compose.dev.yml down
|
| 92 |
+
|
| 93 |
+
docker-logs:
|
| 94 |
+
docker-compose logs -f chat-agent
|
| 95 |
+
|
| 96 |
+
docker-clean:
|
| 97 |
+
docker-compose down -v
|
| 98 |
+
docker system prune -f
|
| 99 |
+
|
| 100 |
+
# Code quality
|
| 101 |
+
lint:
|
| 102 |
+
flake8 chat_agent/ --max-line-length=100 --ignore=E203,W503
|
| 103 |
+
flake8 tests/ --max-line-length=100 --ignore=E203,W503
|
| 104 |
+
|
| 105 |
+
format:
|
| 106 |
+
black chat_agent/ tests/ --line-length=100
|
| 107 |
+
isort chat_agent/ tests/
|
| 108 |
+
|
| 109 |
+
type-check:
|
| 110 |
+
mypy chat_agent/ --ignore-missing-imports
|
| 111 |
+
|
| 112 |
+
# Cleanup
|
| 113 |
+
clean:
|
| 114 |
+
find . -type f -name "*.pyc" -delete
|
| 115 |
+
find . -type d -name "__pycache__" -delete
|
| 116 |
+
find . -type d -name "*.egg-info" -exec rm -rf {} +
|
| 117 |
+
rm -rf .coverage htmlcov/ .pytest_cache/ .mypy_cache/
|
| 118 |
+
rm -rf logs/*.log
|
| 119 |
+
|
| 120 |
+
clean-all: clean
|
| 121 |
+
rm -rf venv/
|
| 122 |
+
rm -rf instance/
|
| 123 |
+
rm -rf flask_session/
|
| 124 |
+
|
| 125 |
+
# Health checks
|
| 126 |
+
health:
|
| 127 |
+
curl -s http://localhost:5000/health/ | python -m json.tool
|
| 128 |
+
|
| 129 |
+
health-detailed:
|
| 130 |
+
curl -s http://localhost:5000/health/detailed | python -m json.tool
|
| 131 |
+
|
| 132 |
+
# Development helpers
|
| 133 |
+
dev-install:
|
| 134 |
+
pip install -r requirements.txt
|
| 135 |
+
pip install flask-debugtoolbar ipdb watchdog black flake8 isort mypy
|
| 136 |
+
|
| 137 |
+
logs:
|
| 138 |
+
tail -f logs/chat_agent.log
|
| 139 |
+
|
| 140 |
+
logs-error:
|
| 141 |
+
tail -f logs/chat_agent.log | grep ERROR
|
| 142 |
+
|
| 143 |
+
# Production deployment helpers
|
| 144 |
+
deploy-check:
|
| 145 |
+
@echo "Pre-deployment checklist:"
|
| 146 |
+
@echo "- [ ] Environment variables configured"
|
| 147 |
+
@echo "- [ ] Database migrations applied"
|
| 148 |
+
@echo "- [ ] SSL certificates installed"
|
| 149 |
+
@echo "- [ ] Firewall configured"
|
| 150 |
+
@echo "- [ ] Monitoring set up"
|
| 151 |
+
@echo "- [ ] Backups configured"
|
| 152 |
+
|
| 153 |
+
backup-db:
|
| 154 |
+
pg_dump $(DATABASE_URL) > backups/backup_$(shell date +%Y%m%d_%H%M%S).sql
|
| 155 |
+
|
| 156 |
+
restore-db:
|
| 157 |
+
@echo "Warning: This will overwrite the current database!"
|
| 158 |
+
@read -p "Enter backup file path: " backup_file; \
|
| 159 |
+
psql $(DATABASE_URL) < $$backup_file
|
| 160 |
+
|
| 161 |
+
# Monitoring
|
| 162 |
+
monitor:
|
| 163 |
+
watch -n 5 'curl -s http://localhost:5000/health/detailed | python -m json.tool'
|
| 164 |
+
|
| 165 |
+
# Documentation
|
| 166 |
+
docs:
|
| 167 |
+
@echo "Documentation available:"
|
| 168 |
+
@echo "- README.md - Project overview"
|
| 169 |
+
@echo "- docs/DEPLOYMENT.md - Deployment guide"
|
| 170 |
+
@echo "- docs/ENVIRONMENT_SETUP.md - Environment setup"
|
| 171 |
+
@echo "- .kiro/specs/multi-language-chat-agent/ - Feature specifications"
|
README.md
CHANGED
|
@@ -1,12 +1,91 @@
|
|
| 1 |
-
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Multi-Language Chat Agent
|
| 2 |
+
|
| 3 |
+
A Flask-based chat agent that supports multiple programming languages with Python as the default. The agent maintains proper chat history, integrates with Groq LangChain API for LLM capabilities, and provides language-specific assistance to students learning to code.
|
| 4 |
+
|
| 5 |
+
## Project Structure
|
| 6 |
+
|
| 7 |
+
```
|
| 8 |
+
chat_agent/
|
| 9 |
+
├── chat_agent/ # Main application package
|
| 10 |
+
│ ├── __init__.py
|
| 11 |
+
│ ├── api/ # REST API endpoints
|
| 12 |
+
│ ├── models/ # Data models
|
| 13 |
+
│ ├── services/ # Business logic services
|
| 14 |
+
│ ├── utils/ # Utility functions
|
| 15 |
+
│ └── websocket/ # WebSocket handlers
|
| 16 |
+
├── static/ # Static assets (CSS, JS)
|
| 17 |
+
│ ├── css/
|
| 18 |
+
│ └── js/
|
| 19 |
+
├── templates/ # HTML templates
|
| 20 |
+
├── tests/ # Test suite
|
| 21 |
+
│ ├── unit/ # Unit tests
|
| 22 |
+
│ └── integration/ # Integration tests
|
| 23 |
+
├── app.py # Application entry point
|
| 24 |
+
├── config.py # Configuration management
|
| 25 |
+
├── requirements.txt # Python dependencies
|
| 26 |
+
└── .env.example # Environment variables template
|
| 27 |
+
```
|
| 28 |
+
|
| 29 |
+
## Setup Instructions
|
| 30 |
+
|
| 31 |
+
1. **Clone and navigate to the project directory**
|
| 32 |
+
|
| 33 |
+
2. **Create a virtual environment**
|
| 34 |
+
```bash
|
| 35 |
+
python -m venv venv
|
| 36 |
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
3. **Install dependencies**
|
| 40 |
+
```bash
|
| 41 |
+
pip install -r requirements.txt
|
| 42 |
+
```
|
| 43 |
+
|
| 44 |
+
4. **Set up environment variables**
|
| 45 |
+
```bash
|
| 46 |
+
cp .env.example .env
|
| 47 |
+
# Edit .env with your actual configuration values
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
5. **Set up databases**
|
| 51 |
+
- Install and start PostgreSQL
|
| 52 |
+
- Install and start Redis
|
| 53 |
+
- Update database connection strings in .env
|
| 54 |
+
|
| 55 |
+
6. **Run the application**
|
| 56 |
+
```bash
|
| 57 |
+
python app.py
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
## Environment Variables
|
| 61 |
+
|
| 62 |
+
Copy `.env.example` to `.env` and configure the following:
|
| 63 |
+
|
| 64 |
+
- `GROQ_API_KEY`: Your Groq API key for LLM integration
|
| 65 |
+
- `DATABASE_URL`: PostgreSQL connection string
|
| 66 |
+
- `REDIS_URL`: Redis connection string
|
| 67 |
+
- `SECRET_KEY`: Flask secret key for sessions
|
| 68 |
+
|
| 69 |
+
## Features
|
| 70 |
+
|
| 71 |
+
- Multi-language programming support (Python, JavaScript, Java, C++, etc.)
|
| 72 |
+
- Real-time chat with WebSocket communication
|
| 73 |
+
- Persistent chat history with Redis caching
|
| 74 |
+
- Groq LangChain API integration
|
| 75 |
+
- Session management for concurrent users
|
| 76 |
+
- Language context switching mid-conversation
|
| 77 |
+
|
| 78 |
+
## Development
|
| 79 |
+
|
| 80 |
+
This project follows the spec-driven development methodology. See the implementation tasks in `.kiro/specs/multi-language-chat-agent/tasks.md` for detailed development steps.
|
| 81 |
+
|
| 82 |
+
## Testing
|
| 83 |
+
|
| 84 |
+
Run tests with:
|
| 85 |
+
```bash
|
| 86 |
+
pytest tests/
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
## License
|
| 90 |
+
|
| 91 |
+
MIT License
|
REDIS_SETUP.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Redis Setup Guide for Chat Agent
|
| 2 |
+
|
| 3 |
+
## Why Redis is Used
|
| 4 |
+
|
| 5 |
+
The chat agent uses Redis for several performance and scalability benefits:
|
| 6 |
+
|
| 7 |
+
1. **Session Caching** - Fast access to frequently used session data
|
| 8 |
+
2. **Chat History Caching** - Quick retrieval of recent messages
|
| 9 |
+
3. **Rate Limiting** - Distributed rate limiting across multiple app instances
|
| 10 |
+
4. **User Session Tracking** - Managing multiple sessions per user
|
| 11 |
+
|
| 12 |
+
## Error 10061 Explanation
|
| 13 |
+
|
| 14 |
+
**Error 10061 connecting to localhost:6379** means:
|
| 15 |
+
- Port 6379 is Redis's default port
|
| 16 |
+
- No Redis server is running on your machine
|
| 17 |
+
- The application can't connect to Redis for caching
|
| 18 |
+
|
| 19 |
+
## Installation Options
|
| 20 |
+
|
| 21 |
+
### Option 1: Windows Native Installation
|
| 22 |
+
|
| 23 |
+
1. **Download Redis for Windows:**
|
| 24 |
+
- Visit: https://github.com/microsoftarchive/redis/releases
|
| 25 |
+
- Download the latest `.msi` installer
|
| 26 |
+
- Run the installer and follow the setup wizard
|
| 27 |
+
|
| 28 |
+
2. **Start Redis:**
|
| 29 |
+
```cmd
|
| 30 |
+
# Redis should start automatically after installation
|
| 31 |
+
# Or manually start it:
|
| 32 |
+
redis-server
|
| 33 |
+
```
|
| 34 |
+
|
| 35 |
+
3. **Verify Installation:**
|
| 36 |
+
```cmd
|
| 37 |
+
redis-cli ping
|
| 38 |
+
# Should return: PONG
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
### Option 2: Using Chocolatey (Windows)
|
| 42 |
+
|
| 43 |
+
```cmd
|
| 44 |
+
# Install Chocolatey first if you don't have it
|
| 45 |
+
# Then install Redis:
|
| 46 |
+
choco install redis-64
|
| 47 |
+
|
| 48 |
+
# Start Redis
|
| 49 |
+
redis-server
|
| 50 |
+
|
| 51 |
+
# Test connection
|
| 52 |
+
redis-cli ping
|
| 53 |
+
```
|
| 54 |
+
|
| 55 |
+
### Option 3: Using Docker (Recommended)
|
| 56 |
+
|
| 57 |
+
```bash
|
| 58 |
+
# Pull and run Redis container
|
| 59 |
+
docker run -d -p 6379:6379 --name redis redis:alpine
|
| 60 |
+
|
| 61 |
+
# Verify it's running
|
| 62 |
+
docker ps
|
| 63 |
+
|
| 64 |
+
# Test connection
|
| 65 |
+
docker exec -it redis redis-cli ping
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
### Option 4: WSL/Linux
|
| 69 |
+
|
| 70 |
+
```bash
|
| 71 |
+
# Update package list
|
| 72 |
+
sudo apt update
|
| 73 |
+
|
| 74 |
+
# Install Redis
|
| 75 |
+
sudo apt install redis-server
|
| 76 |
+
|
| 77 |
+
# Start Redis service
|
| 78 |
+
sudo systemctl start redis-server
|
| 79 |
+
|
| 80 |
+
# Enable auto-start on boot
|
| 81 |
+
sudo systemctl enable redis-server
|
| 82 |
+
|
| 83 |
+
# Test connection
|
| 84 |
+
redis-cli ping
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
## Configuration
|
| 88 |
+
|
| 89 |
+
### Environment Variables
|
| 90 |
+
|
| 91 |
+
Create a `.env` file in your project root:
|
| 92 |
+
|
| 93 |
+
```env
|
| 94 |
+
# Redis Configuration
|
| 95 |
+
REDIS_URL=redis://localhost:6379/0
|
| 96 |
+
REDIS_HOST=localhost
|
| 97 |
+
REDIS_PORT=6379
|
| 98 |
+
REDIS_DB=0
|
| 99 |
+
REDIS_PASSWORD=
|
| 100 |
+
|
| 101 |
+
# For production with password:
|
| 102 |
+
# REDIS_URL=redis://:password@localhost:6379/0
|
| 103 |
+
# REDIS_PASSWORD=your-secure-password
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
### Testing Configuration
|
| 107 |
+
|
| 108 |
+
For testing without Redis, set:
|
| 109 |
+
|
| 110 |
+
```env
|
| 111 |
+
REDIS_URL=None
|
| 112 |
+
```
|
| 113 |
+
|
| 114 |
+
Or use the testing configuration which automatically disables Redis.
|
| 115 |
+
|
| 116 |
+
## Running Without Redis
|
| 117 |
+
|
| 118 |
+
The application is designed to work without Redis, but with reduced performance:
|
| 119 |
+
|
| 120 |
+
### What Works Without Redis:
|
| 121 |
+
- ✅ All API endpoints function normally
|
| 122 |
+
- ✅ Session management (database-only)
|
| 123 |
+
- ✅ Chat history (database-only)
|
| 124 |
+
- ✅ Language context management
|
| 125 |
+
- ✅ Authentication and authorization
|
| 126 |
+
|
| 127 |
+
### What's Affected Without Redis:
|
| 128 |
+
- ⚠️ **Performance**: Slower session and message retrieval
|
| 129 |
+
- ⚠️ **Rate Limiting**: Uses in-memory storage (not distributed)
|
| 130 |
+
- ⚠️ **Caching**: No caching layer, all requests hit the database
|
| 131 |
+
- ⚠️ **Scalability**: Cannot scale across multiple app instances
|
| 132 |
+
|
| 133 |
+
### Performance Impact:
|
| 134 |
+
- Session retrieval: ~50-100ms slower per request
|
| 135 |
+
- Chat history: ~100-200ms slower for large histories
|
| 136 |
+
- Rate limiting: Resets when app restarts
|
| 137 |
+
|
| 138 |
+
## Production Recommendations
|
| 139 |
+
|
| 140 |
+
### Redis Configuration for Production:
|
| 141 |
+
|
| 142 |
+
1. **Use a dedicated Redis instance**
|
| 143 |
+
2. **Enable password authentication**
|
| 144 |
+
3. **Configure persistence** (RDB + AOF)
|
| 145 |
+
4. **Set up monitoring**
|
| 146 |
+
5. **Use Redis Cluster** for high availability
|
| 147 |
+
|
| 148 |
+
### Example Production Config:
|
| 149 |
+
|
| 150 |
+
```env
|
| 151 |
+
# Production Redis with authentication
|
| 152 |
+
REDIS_URL=redis://:your-secure-password@redis-server:6379/0
|
| 153 |
+
REDIS_PASSWORD=your-secure-password
|
| 154 |
+
|
| 155 |
+
# Connection pool settings
|
| 156 |
+
REDIS_MAX_CONNECTIONS=20
|
| 157 |
+
REDIS_SOCKET_TIMEOUT=5
|
| 158 |
+
REDIS_SOCKET_CONNECT_TIMEOUT=5
|
| 159 |
+
```
|
| 160 |
+
|
| 161 |
+
## Troubleshooting
|
| 162 |
+
|
| 163 |
+
### Common Issues:
|
| 164 |
+
|
| 165 |
+
1. **"Connection refused" (Error 10061)**
|
| 166 |
+
- Redis server is not running
|
| 167 |
+
- Wrong host/port configuration
|
| 168 |
+
- Firewall blocking the connection
|
| 169 |
+
|
| 170 |
+
2. **"Authentication failed"**
|
| 171 |
+
- Wrong password in REDIS_URL
|
| 172 |
+
- Redis configured with auth but no password provided
|
| 173 |
+
|
| 174 |
+
3. **"Connection timeout"**
|
| 175 |
+
- Network issues
|
| 176 |
+
- Redis server overloaded
|
| 177 |
+
- Wrong host address
|
| 178 |
+
|
| 179 |
+
### Debug Commands:
|
| 180 |
+
|
| 181 |
+
```bash
|
| 182 |
+
# Check if Redis is running
|
| 183 |
+
redis-cli ping
|
| 184 |
+
|
| 185 |
+
# Check Redis info
|
| 186 |
+
redis-cli info
|
| 187 |
+
|
| 188 |
+
# Monitor Redis commands
|
| 189 |
+
redis-cli monitor
|
| 190 |
+
|
| 191 |
+
# Check specific database
|
| 192 |
+
redis-cli -n 0 keys "*"
|
| 193 |
+
```
|
| 194 |
+
|
| 195 |
+
## Development vs Production
|
| 196 |
+
|
| 197 |
+
### Development (Local):
|
| 198 |
+
```env
|
| 199 |
+
REDIS_URL=redis://localhost:6379/0
|
| 200 |
+
```
|
| 201 |
+
|
| 202 |
+
### Testing (No Redis):
|
| 203 |
+
```env
|
| 204 |
+
REDIS_URL=None
|
| 205 |
+
```
|
| 206 |
+
|
| 207 |
+
### Production (Managed Redis):
|
| 208 |
+
```env
|
| 209 |
+
REDIS_URL=redis://:password@your-redis-host:6379/0
|
| 210 |
+
```
|
| 211 |
+
|
| 212 |
+
## Cloud Redis Services
|
| 213 |
+
|
| 214 |
+
For production, consider managed Redis services:
|
| 215 |
+
|
| 216 |
+
- **AWS ElastiCache**
|
| 217 |
+
- **Google Cloud Memorystore**
|
| 218 |
+
- **Azure Cache for Redis**
|
| 219 |
+
- **Redis Cloud**
|
| 220 |
+
- **DigitalOcean Managed Redis**
|
| 221 |
+
|
| 222 |
+
These provide:
|
| 223 |
+
- Automatic backups
|
| 224 |
+
- High availability
|
| 225 |
+
- Monitoring and alerts
|
| 226 |
+
- Security and compliance
|
| 227 |
+
- Automatic scaling
|
| 228 |
+
|
| 229 |
+
## Summary
|
| 230 |
+
|
| 231 |
+
The **Error 10061** occurs because Redis is not installed or running. You have three options:
|
| 232 |
+
|
| 233 |
+
1. **Install Redis** (recommended for development/production)
|
| 234 |
+
2. **Use Docker** to run Redis (easiest setup)
|
| 235 |
+
3. **Run without Redis** (works but with performance impact)
|
| 236 |
+
|
| 237 |
+
The application gracefully handles Redis being unavailable and will continue to function using database-only operations.
|
Refine Scratch Block Builders_.docx
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:40b2b72cc62775bc1e412b0c6985185d36c49e4239f333d3d64f861cc93bb01e
|
| 3 |
+
size 6226907
|
app.py
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Main application entry point for the multi-language chat agent."""
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
from flask import Flask
|
| 5 |
+
from flask_socketio import SocketIO
|
| 6 |
+
from flask_session import Session
|
| 7 |
+
import redis
|
| 8 |
+
|
| 9 |
+
from config import config
|
| 10 |
+
from chat_agent.models.base import db
|
| 11 |
+
from chat_agent.utils.logging_config import setup_logging
|
| 12 |
+
from chat_agent.utils.error_handler import set_error_handler, ErrorHandler
|
| 13 |
+
from chat_agent.utils.connection_pool import initialize_connection_pools, get_connection_pool_manager
|
| 14 |
+
from chat_agent.services.cache_service import initialize_cache_service, get_cache_service
|
| 15 |
+
from chat_agent.utils.response_optimization import ResponseMiddleware
|
| 16 |
+
|
| 17 |
+
# Initialize extensions
|
| 18 |
+
socketio = SocketIO()
|
| 19 |
+
session = Session()
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def create_app(config_name=None):
|
| 23 |
+
"""Application factory pattern."""
|
| 24 |
+
if config_name is None:
|
| 25 |
+
config_name = os.getenv('FLASK_ENV', 'development')
|
| 26 |
+
|
| 27 |
+
app = Flask(__name__)
|
| 28 |
+
app.config.from_object(config[config_name])
|
| 29 |
+
|
| 30 |
+
# Setup comprehensive logging
|
| 31 |
+
loggers = setup_logging("chat_agent", app.config.get('LOG_LEVEL', 'INFO'))
|
| 32 |
+
app.logger = loggers['main']
|
| 33 |
+
|
| 34 |
+
# Setup global error handler
|
| 35 |
+
error_handler = ErrorHandler(loggers['error'])
|
| 36 |
+
set_error_handler(error_handler)
|
| 37 |
+
|
| 38 |
+
app.logger.info("Chat agent application starting", extra={
|
| 39 |
+
'config': config_name,
|
| 40 |
+
'debug': app.config.get('DEBUG', False),
|
| 41 |
+
'logging_level': app.config.get('LOG_LEVEL', 'INFO')
|
| 42 |
+
})
|
| 43 |
+
|
| 44 |
+
# Initialize connection pools for performance optimization
|
| 45 |
+
database_url = app.config.get('SQLALCHEMY_DATABASE_URI')
|
| 46 |
+
redis_url = app.config.get('REDIS_URL')
|
| 47 |
+
|
| 48 |
+
connection_pool_manager = initialize_connection_pools(database_url, redis_url)
|
| 49 |
+
|
| 50 |
+
# Configure SQLAlchemy to use connection pool
|
| 51 |
+
if connection_pool_manager:
|
| 52 |
+
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
|
| 53 |
+
'pool_size': int(os.getenv('DB_POOL_SIZE', '10')),
|
| 54 |
+
'max_overflow': int(os.getenv('DB_MAX_OVERFLOW', '20')),
|
| 55 |
+
'pool_recycle': int(os.getenv('DB_POOL_RECYCLE', '3600')),
|
| 56 |
+
'pool_pre_ping': True,
|
| 57 |
+
'pool_timeout': int(os.getenv('DB_POOL_TIMEOUT', '30'))
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
# Initialize extensions with app
|
| 61 |
+
db.init_app(app)
|
| 62 |
+
socketio.init_app(app, cors_allowed_origins="*")
|
| 63 |
+
|
| 64 |
+
# Initialize response optimization middleware
|
| 65 |
+
ResponseMiddleware(app)
|
| 66 |
+
|
| 67 |
+
# Configure Redis for sessions and caching (if available)
|
| 68 |
+
redis_client = None
|
| 69 |
+
if redis_url and redis_url != 'None':
|
| 70 |
+
try:
|
| 71 |
+
# Use connection pool manager's Redis client if available
|
| 72 |
+
if connection_pool_manager:
|
| 73 |
+
redis_client = connection_pool_manager.get_redis_client()
|
| 74 |
+
else:
|
| 75 |
+
redis_client = redis.from_url(redis_url)
|
| 76 |
+
|
| 77 |
+
if redis_client:
|
| 78 |
+
redis_client.ping() # Test connection
|
| 79 |
+
app.config['SESSION_REDIS'] = redis_client
|
| 80 |
+
session.init_app(app)
|
| 81 |
+
app.logger.info("Redis connection established for sessions and caching")
|
| 82 |
+
else:
|
| 83 |
+
raise Exception("Redis client not available")
|
| 84 |
+
|
| 85 |
+
except Exception as e:
|
| 86 |
+
app.logger.warning(f"Redis connection failed: {e}. Sessions will use filesystem.")
|
| 87 |
+
app.config['SESSION_TYPE'] = 'filesystem'
|
| 88 |
+
session.init_app(app)
|
| 89 |
+
redis_client = None
|
| 90 |
+
else:
|
| 91 |
+
app.logger.info("Redis disabled. Using filesystem sessions.")
|
| 92 |
+
app.config['SESSION_TYPE'] = 'filesystem'
|
| 93 |
+
session.init_app(app)
|
| 94 |
+
|
| 95 |
+
# Initialize cache service with Redis client
|
| 96 |
+
cache_service = initialize_cache_service(redis_client)
|
| 97 |
+
app.logger.info(f"Cache service initialized", extra={
|
| 98 |
+
'redis_enabled': bool(redis_client)
|
| 99 |
+
})
|
| 100 |
+
|
| 101 |
+
# Register API blueprints
|
| 102 |
+
from chat_agent.api import chat_bp, create_limiter, setup_error_handlers, RequestLoggingMiddleware
|
| 103 |
+
from chat_agent.api.health import health_bp
|
| 104 |
+
from chat_agent.api.performance_routes import performance_bp
|
| 105 |
+
app.register_blueprint(chat_bp)
|
| 106 |
+
app.register_blueprint(health_bp)
|
| 107 |
+
app.register_blueprint(performance_bp)
|
| 108 |
+
|
| 109 |
+
# Configure rate limiting
|
| 110 |
+
limiter = create_limiter(app)
|
| 111 |
+
if redis_url and redis_url != 'None':
|
| 112 |
+
limiter.storage_uri = redis_url
|
| 113 |
+
# If no Redis, limiter will use in-memory storage (with warning)
|
| 114 |
+
|
| 115 |
+
# Setup error handlers
|
| 116 |
+
setup_error_handlers(app)
|
| 117 |
+
|
| 118 |
+
# Setup request logging middleware
|
| 119 |
+
RequestLoggingMiddleware(app)
|
| 120 |
+
|
| 121 |
+
# Add chat interface route
|
| 122 |
+
@app.route('/')
|
| 123 |
+
@app.route('/chat')
|
| 124 |
+
def chat_interface():
|
| 125 |
+
"""Serve the chat interface."""
|
| 126 |
+
from flask import render_template
|
| 127 |
+
return render_template('chat.html')
|
| 128 |
+
|
| 129 |
+
# Initialize real chat agent services
|
| 130 |
+
from chat_agent.services.groq_client import GroqClient
|
| 131 |
+
from chat_agent.services.language_context import LanguageContextManager
|
| 132 |
+
from chat_agent.services.session_manager import SessionManager
|
| 133 |
+
from chat_agent.services.chat_history import ChatHistoryManager
|
| 134 |
+
from chat_agent.services.chat_agent import ChatAgent
|
| 135 |
+
from chat_agent.services.programming_assistance import ProgrammingAssistanceService
|
| 136 |
+
|
| 137 |
+
# Initialize services
|
| 138 |
+
try:
|
| 139 |
+
# Initialize Redis client
|
| 140 |
+
redis_url = app.config.get('REDIS_URL', 'redis://localhost:6379/0')
|
| 141 |
+
redis_client = redis.from_url(redis_url)
|
| 142 |
+
|
| 143 |
+
# Test Redis connection
|
| 144 |
+
redis_client.ping()
|
| 145 |
+
print("✅ Redis connection successful")
|
| 146 |
+
|
| 147 |
+
groq_client = GroqClient()
|
| 148 |
+
language_context_manager = LanguageContextManager()
|
| 149 |
+
session_manager = SessionManager(redis_client)
|
| 150 |
+
chat_history_manager = ChatHistoryManager(redis_client)
|
| 151 |
+
programming_assistance_service = ProgrammingAssistanceService()
|
| 152 |
+
|
| 153 |
+
# Initialize main chat agent
|
| 154 |
+
chat_agent = ChatAgent(
|
| 155 |
+
groq_client=groq_client,
|
| 156 |
+
language_context_manager=language_context_manager,
|
| 157 |
+
session_manager=session_manager,
|
| 158 |
+
chat_history_manager=chat_history_manager,
|
| 159 |
+
programming_assistance_service=programming_assistance_service
|
| 160 |
+
)
|
| 161 |
+
|
| 162 |
+
print("✅ Chat agent services initialized successfully")
|
| 163 |
+
|
| 164 |
+
except Exception as e:
|
| 165 |
+
print(f"⚠️ Error initializing chat agent services: {e}")
|
| 166 |
+
print("🔄 Falling back to demo mode")
|
| 167 |
+
chat_agent = None
|
| 168 |
+
|
| 169 |
+
# Store session mapping for WebSocket connections
|
| 170 |
+
websocket_sessions = {}
|
| 171 |
+
|
| 172 |
+
# Initialize WebSocket handlers for chat interface
|
| 173 |
+
@socketio.on('connect')
|
| 174 |
+
def handle_connect(auth=None):
|
| 175 |
+
"""Handle WebSocket connection for chat interface."""
|
| 176 |
+
from flask_socketio import emit
|
| 177 |
+
from flask import request
|
| 178 |
+
import uuid
|
| 179 |
+
|
| 180 |
+
try:
|
| 181 |
+
# Create a new session for this connection
|
| 182 |
+
user_id = f"user_{request.sid}" # Use socket ID as user ID for demo
|
| 183 |
+
|
| 184 |
+
if chat_agent and session_manager:
|
| 185 |
+
session = session_manager.create_session(user_id, language='python')
|
| 186 |
+
websocket_sessions[request.sid] = session.id
|
| 187 |
+
|
| 188 |
+
emit('connection_status', {
|
| 189 |
+
'status': 'connected',
|
| 190 |
+
'session_id': session.id,
|
| 191 |
+
'language': session.language,
|
| 192 |
+
'message_count': session.message_count,
|
| 193 |
+
'timestamp': datetime.now().isoformat()
|
| 194 |
+
})
|
| 195 |
+
|
| 196 |
+
print(f"WebSocket connected: session={session.id}, user={user_id}")
|
| 197 |
+
else:
|
| 198 |
+
# Fallback to demo mode
|
| 199 |
+
session_id = str(uuid.uuid4())
|
| 200 |
+
websocket_sessions[request.sid] = session_id
|
| 201 |
+
|
| 202 |
+
emit('connection_status', {
|
| 203 |
+
'status': 'connected',
|
| 204 |
+
'session_id': session_id,
|
| 205 |
+
'language': 'python',
|
| 206 |
+
'message_count': 0,
|
| 207 |
+
'timestamp': datetime.now().isoformat()
|
| 208 |
+
})
|
| 209 |
+
|
| 210 |
+
print(f"WebSocket connected (demo mode): session={session_id}, user={user_id}")
|
| 211 |
+
|
| 212 |
+
except Exception as e:
|
| 213 |
+
print(f"Error connecting WebSocket: {e}")
|
| 214 |
+
emit('error', {'message': 'Connection failed', 'code': 'CONNECTION_ERROR'})
|
| 215 |
+
|
| 216 |
+
@socketio.on('disconnect')
|
| 217 |
+
def handle_disconnect(reason=None):
|
| 218 |
+
"""Handle WebSocket disconnection."""
|
| 219 |
+
from flask import request
|
| 220 |
+
|
| 221 |
+
# Clean up session mapping
|
| 222 |
+
if request.sid in websocket_sessions:
|
| 223 |
+
session_id = websocket_sessions[request.sid]
|
| 224 |
+
del websocket_sessions[request.sid]
|
| 225 |
+
print(f"WebSocket disconnected: session={session_id}")
|
| 226 |
+
else:
|
| 227 |
+
print("WebSocket disconnected")
|
| 228 |
+
|
| 229 |
+
@socketio.on('message')
|
| 230 |
+
def handle_message(data):
|
| 231 |
+
"""Handle chat messages using real chat agent."""
|
| 232 |
+
from flask_socketio import emit
|
| 233 |
+
from flask import request
|
| 234 |
+
|
| 235 |
+
try:
|
| 236 |
+
print(f"Received message: {data}")
|
| 237 |
+
|
| 238 |
+
# Get session ID for this connection
|
| 239 |
+
if request.sid not in websocket_sessions:
|
| 240 |
+
emit('error', {'message': 'No active session', 'code': 'NO_SESSION'})
|
| 241 |
+
return
|
| 242 |
+
|
| 243 |
+
session_id = websocket_sessions[request.sid]
|
| 244 |
+
content = data.get('content', '').strip()
|
| 245 |
+
language = data.get('language', 'python')
|
| 246 |
+
|
| 247 |
+
if not content:
|
| 248 |
+
emit('error', {'message': 'Empty message received', 'code': 'EMPTY_MESSAGE'})
|
| 249 |
+
return
|
| 250 |
+
|
| 251 |
+
# Process message with real chat agent
|
| 252 |
+
emit('response_start', {
|
| 253 |
+
'session_id': session_id,
|
| 254 |
+
'language': language,
|
| 255 |
+
'timestamp': datetime.now().isoformat()
|
| 256 |
+
})
|
| 257 |
+
|
| 258 |
+
try:
|
| 259 |
+
if chat_agent:
|
| 260 |
+
# Use the real chat agent to process the message
|
| 261 |
+
print(f"🤖 Processing message with chat agent: '{content}' (language: {language})")
|
| 262 |
+
result = chat_agent.process_message(session_id, content, language)
|
| 263 |
+
|
| 264 |
+
# Extract response content from the result dictionary
|
| 265 |
+
if isinstance(result, dict) and 'response' in result:
|
| 266 |
+
response = result['response']
|
| 267 |
+
print(f"✅ Chat agent response: {response[:100]}..." if len(response) > 100 else f"✅ Chat agent response: {response}")
|
| 268 |
+
else:
|
| 269 |
+
print(f"✅ Unexpected response format: {type(result)}, value: {result}")
|
| 270 |
+
response = str(result)
|
| 271 |
+
else:
|
| 272 |
+
# Fallback response if chat agent is not available
|
| 273 |
+
response = f"I understand you're asking about: '{content}'. I'm currently in demo mode, but I can help you with {language} programming concepts, debugging, and best practices. The full AI-powered assistant will provide more detailed responses."
|
| 274 |
+
|
| 275 |
+
# Ensure response is a string before processing
|
| 276 |
+
if not isinstance(response, str):
|
| 277 |
+
response = str(response)
|
| 278 |
+
|
| 279 |
+
# Send response in chunks to simulate streaming
|
| 280 |
+
words = response.split()
|
| 281 |
+
chunk_size = 5
|
| 282 |
+
total_chunks = (len(words) + chunk_size - 1) // chunk_size
|
| 283 |
+
|
| 284 |
+
for i in range(0, len(words), chunk_size):
|
| 285 |
+
chunk = ' '.join(words[i:i+chunk_size]) + ' '
|
| 286 |
+
emit('response_chunk', {
|
| 287 |
+
'content': chunk,
|
| 288 |
+
'timestamp': datetime.now().isoformat()
|
| 289 |
+
})
|
| 290 |
+
socketio.sleep(0.02) # Small delay for streaming effect
|
| 291 |
+
|
| 292 |
+
emit('response_complete', {
|
| 293 |
+
'message_id': str(uuid.uuid4()),
|
| 294 |
+
'total_chunks': total_chunks,
|
| 295 |
+
'processing_time': 1.0,
|
| 296 |
+
'timestamp': datetime.now().isoformat()
|
| 297 |
+
})
|
| 298 |
+
|
| 299 |
+
except Exception as e:
|
| 300 |
+
print(f"❌ Error processing message with chat agent: {e}")
|
| 301 |
+
# Fallback to demo response if chat agent fails
|
| 302 |
+
demo_response = f"I apologize, but I'm having trouble processing your request right now. You asked about: '{content}'. Please try again in a moment, or check that the Groq API key is properly configured."
|
| 303 |
+
|
| 304 |
+
emit('response_chunk', {
|
| 305 |
+
'content': demo_response,
|
| 306 |
+
'timestamp': datetime.now().isoformat()
|
| 307 |
+
})
|
| 308 |
+
|
| 309 |
+
emit('response_complete', {
|
| 310 |
+
'message_id': str(uuid.uuid4()),
|
| 311 |
+
'total_chunks': 1,
|
| 312 |
+
'processing_time': 0.1,
|
| 313 |
+
'timestamp': datetime.now().isoformat()
|
| 314 |
+
})
|
| 315 |
+
|
| 316 |
+
except Exception as e:
|
| 317 |
+
print(f"Error handling message: {e}")
|
| 318 |
+
emit('error', {'message': 'Failed to process message', 'code': 'PROCESSING_ERROR'})
|
| 319 |
+
|
| 320 |
+
@socketio.on('language_switch')
|
| 321 |
+
def handle_language_switch(data):
|
| 322 |
+
"""Handle language switching using real chat agent."""
|
| 323 |
+
from flask_socketio import emit
|
| 324 |
+
from flask import request
|
| 325 |
+
|
| 326 |
+
try:
|
| 327 |
+
# Get session ID for this connection
|
| 328 |
+
if request.sid not in websocket_sessions:
|
| 329 |
+
emit('error', {'message': 'No active session', 'code': 'NO_SESSION'})
|
| 330 |
+
return
|
| 331 |
+
|
| 332 |
+
session_id = websocket_sessions[request.sid]
|
| 333 |
+
new_language = data.get('language', 'python')
|
| 334 |
+
|
| 335 |
+
language_names = {
|
| 336 |
+
'python': 'Python',
|
| 337 |
+
'javascript': 'JavaScript',
|
| 338 |
+
'java': 'Java',
|
| 339 |
+
'cpp': 'C++',
|
| 340 |
+
'csharp': 'C#',
|
| 341 |
+
'go': 'Go',
|
| 342 |
+
'rust': 'Rust',
|
| 343 |
+
'typescript': 'TypeScript'
|
| 344 |
+
}
|
| 345 |
+
|
| 346 |
+
try:
|
| 347 |
+
if chat_agent:
|
| 348 |
+
# Use real chat agent to switch language
|
| 349 |
+
result = chat_agent.switch_language(session_id, new_language)
|
| 350 |
+
|
| 351 |
+
emit('language_switched', {
|
| 352 |
+
'previous_language': result.get('previous_language', 'python'),
|
| 353 |
+
'new_language': result.get('new_language', new_language),
|
| 354 |
+
'message': result.get('message', f'Language switched to {language_names.get(new_language, new_language)}'),
|
| 355 |
+
'timestamp': datetime.now().isoformat()
|
| 356 |
+
})
|
| 357 |
+
|
| 358 |
+
print(f"🔄 Language switched to: {new_language} for session {session_id}")
|
| 359 |
+
else:
|
| 360 |
+
# Fallback for demo mode
|
| 361 |
+
emit('language_switched', {
|
| 362 |
+
'previous_language': 'python',
|
| 363 |
+
'new_language': new_language,
|
| 364 |
+
'message': f"Switched to {language_names.get(new_language, new_language)}. I'm now ready to help you with {language_names.get(new_language, new_language)} programming!",
|
| 365 |
+
'timestamp': datetime.now().isoformat()
|
| 366 |
+
})
|
| 367 |
+
|
| 368 |
+
print(f"🔄 Language switched to: {new_language} (demo mode)")
|
| 369 |
+
|
| 370 |
+
except Exception as e:
|
| 371 |
+
print(f"❌ Error switching language: {e}")
|
| 372 |
+
emit('error', {'message': 'Failed to switch language', 'code': 'LANGUAGE_SWITCH_ERROR'})
|
| 373 |
+
|
| 374 |
+
except Exception as e:
|
| 375 |
+
print(f"Error handling language switch: {e}")
|
| 376 |
+
emit('error', {'message': 'Failed to switch language', 'code': 'LANGUAGE_SWITCH_ERROR'})
|
| 377 |
+
|
| 378 |
+
# Add error handlers for WebSocket
|
| 379 |
+
@socketio.on_error_default
|
| 380 |
+
def default_error_handler(e):
|
| 381 |
+
"""Handle WebSocket errors."""
|
| 382 |
+
print(f"WebSocket error: {e}")
|
| 383 |
+
from flask_socketio import emit
|
| 384 |
+
emit('error', {'message': 'Connection error occurred', 'code': 'WEBSOCKET_ERROR'})
|
| 385 |
+
|
| 386 |
+
# Import datetime for timestamps
|
| 387 |
+
from datetime import datetime
|
| 388 |
+
import uuid
|
| 389 |
+
|
| 390 |
+
return app
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
if __name__ == '__main__':
|
| 394 |
+
app = create_app()
|
| 395 |
+
socketio.run(app, debug=True, host='0.0.0.0', port=5000)
|
blocks/blocks.json
ADDED
|
@@ -0,0 +1,2221 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"motion_movesteps": {
|
| 3 |
+
"opcode": "motion_movesteps",
|
| 4 |
+
"next": null,
|
| 5 |
+
"parent": null,
|
| 6 |
+
"inputs": {
|
| 7 |
+
"STEPS": [
|
| 8 |
+
1,
|
| 9 |
+
[
|
| 10 |
+
4,
|
| 11 |
+
"10"
|
| 12 |
+
]
|
| 13 |
+
]
|
| 14 |
+
},
|
| 15 |
+
"fields": {},
|
| 16 |
+
"shadow": false,
|
| 17 |
+
"topLevel": true,
|
| 18 |
+
"x": 464,
|
| 19 |
+
"y": -416
|
| 20 |
+
},
|
| 21 |
+
"motion_turnright": {
|
| 22 |
+
"opcode": "motion_turnright",
|
| 23 |
+
"next": null,
|
| 24 |
+
"parent": null,
|
| 25 |
+
"inputs": {
|
| 26 |
+
"DEGREES": [
|
| 27 |
+
1,
|
| 28 |
+
[
|
| 29 |
+
4,
|
| 30 |
+
"15"
|
| 31 |
+
]
|
| 32 |
+
]
|
| 33 |
+
},
|
| 34 |
+
"fields": {},
|
| 35 |
+
"shadow": false,
|
| 36 |
+
"topLevel": true,
|
| 37 |
+
"x": 467,
|
| 38 |
+
"y": -316
|
| 39 |
+
},
|
| 40 |
+
"motion_turnleft": {
|
| 41 |
+
"opcode": "motion_turnleft",
|
| 42 |
+
"next": null,
|
| 43 |
+
"parent": null,
|
| 44 |
+
"inputs": {
|
| 45 |
+
"DEGREES": [
|
| 46 |
+
1,
|
| 47 |
+
[
|
| 48 |
+
4,
|
| 49 |
+
"15"
|
| 50 |
+
]
|
| 51 |
+
]
|
| 52 |
+
},
|
| 53 |
+
"fields": {},
|
| 54 |
+
"shadow": false,
|
| 55 |
+
"topLevel": true,
|
| 56 |
+
"x": 464,
|
| 57 |
+
"y": -210
|
| 58 |
+
},
|
| 59 |
+
"motion_goto": {
|
| 60 |
+
"opcode": "motion_goto",
|
| 61 |
+
"next": null,
|
| 62 |
+
"parent": null,
|
| 63 |
+
"inputs": {
|
| 64 |
+
"TO": [
|
| 65 |
+
1,
|
| 66 |
+
"@iM=Z?~GCbpC}gT7KAKY"
|
| 67 |
+
]
|
| 68 |
+
},
|
| 69 |
+
"fields": {},
|
| 70 |
+
"shadow": false,
|
| 71 |
+
"topLevel": true,
|
| 72 |
+
"x": 465,
|
| 73 |
+
"y": -95
|
| 74 |
+
},
|
| 75 |
+
"motion_goto_menu": {
|
| 76 |
+
"opcode": "motion_goto_menu",
|
| 77 |
+
"next": null,
|
| 78 |
+
"parent": "d|J?C902/xy6tD5,|dmB",
|
| 79 |
+
"inputs": {},
|
| 80 |
+
"fields": {
|
| 81 |
+
"TO": [
|
| 82 |
+
"_random_",
|
| 83 |
+
null
|
| 84 |
+
]
|
| 85 |
+
},
|
| 86 |
+
"shadow": true,
|
| 87 |
+
"topLevel": false
|
| 88 |
+
},
|
| 89 |
+
"motion_gotoxy": {
|
| 90 |
+
"opcode": "motion_gotoxy",
|
| 91 |
+
"next": null,
|
| 92 |
+
"parent": null,
|
| 93 |
+
"inputs": {
|
| 94 |
+
"X": [
|
| 95 |
+
1,
|
| 96 |
+
[
|
| 97 |
+
4,
|
| 98 |
+
"0"
|
| 99 |
+
]
|
| 100 |
+
],
|
| 101 |
+
"Y": [
|
| 102 |
+
1,
|
| 103 |
+
[
|
| 104 |
+
4,
|
| 105 |
+
"0"
|
| 106 |
+
]
|
| 107 |
+
]
|
| 108 |
+
},
|
| 109 |
+
"fields": {},
|
| 110 |
+
"shadow": false,
|
| 111 |
+
"topLevel": true,
|
| 112 |
+
"x": 468,
|
| 113 |
+
"y": 12
|
| 114 |
+
},
|
| 115 |
+
"motion_glideto": {
|
| 116 |
+
"opcode": "motion_glideto",
|
| 117 |
+
"next": null,
|
| 118 |
+
"parent": null,
|
| 119 |
+
"inputs": {
|
| 120 |
+
"SECS": [
|
| 121 |
+
1,
|
| 122 |
+
[
|
| 123 |
+
4,
|
| 124 |
+
"1"
|
| 125 |
+
]
|
| 126 |
+
],
|
| 127 |
+
"TO": [
|
| 128 |
+
1,
|
| 129 |
+
"{id to destination position}"
|
| 130 |
+
]
|
| 131 |
+
},
|
| 132 |
+
"fields": {},
|
| 133 |
+
"shadow": false,
|
| 134 |
+
"topLevel": true,
|
| 135 |
+
"x": 470,
|
| 136 |
+
"y": 129
|
| 137 |
+
},
|
| 138 |
+
"motion_glideto_menu": {
|
| 139 |
+
"opcode": "motion_glideto_menu",
|
| 140 |
+
"next": null,
|
| 141 |
+
"parent": null,
|
| 142 |
+
"inputs": {},
|
| 143 |
+
"fields": {
|
| 144 |
+
"TO": [
|
| 145 |
+
"_random_",
|
| 146 |
+
null
|
| 147 |
+
]
|
| 148 |
+
},
|
| 149 |
+
"shadow": true,
|
| 150 |
+
"topLevel": false
|
| 151 |
+
},
|
| 152 |
+
"motion_glidesecstoxy": {
|
| 153 |
+
"opcode": "motion_glidesecstoxy",
|
| 154 |
+
"next": null,
|
| 155 |
+
"parent": null,
|
| 156 |
+
"inputs": {
|
| 157 |
+
"SECS": [
|
| 158 |
+
1,
|
| 159 |
+
[
|
| 160 |
+
4,
|
| 161 |
+
"1"
|
| 162 |
+
]
|
| 163 |
+
],
|
| 164 |
+
"X": [
|
| 165 |
+
1,
|
| 166 |
+
[
|
| 167 |
+
4,
|
| 168 |
+
"0"
|
| 169 |
+
]
|
| 170 |
+
],
|
| 171 |
+
"Y": [
|
| 172 |
+
1,
|
| 173 |
+
[
|
| 174 |
+
4,
|
| 175 |
+
"0"
|
| 176 |
+
]
|
| 177 |
+
]
|
| 178 |
+
},
|
| 179 |
+
"fields": {},
|
| 180 |
+
"shadow": false,
|
| 181 |
+
"topLevel": true,
|
| 182 |
+
"x": 476,
|
| 183 |
+
"y": 239
|
| 184 |
+
},
|
| 185 |
+
"motion_pointindirection": {
|
| 186 |
+
"opcode": "motion_pointindirection",
|
| 187 |
+
"next": null,
|
| 188 |
+
"parent": null,
|
| 189 |
+
"inputs": {
|
| 190 |
+
"DIRECTION": [
|
| 191 |
+
1,
|
| 192 |
+
[
|
| 193 |
+
8,
|
| 194 |
+
"90"
|
| 195 |
+
]
|
| 196 |
+
]
|
| 197 |
+
},
|
| 198 |
+
"fields": {},
|
| 199 |
+
"shadow": false,
|
| 200 |
+
"topLevel": true,
|
| 201 |
+
"x": 493,
|
| 202 |
+
"y": 361
|
| 203 |
+
},
|
| 204 |
+
"motion_pointtowards": {
|
| 205 |
+
"opcode": "motion_pointtowards",
|
| 206 |
+
"next": null,
|
| 207 |
+
"parent": null,
|
| 208 |
+
"inputs": {
|
| 209 |
+
"TOWARDS": [
|
| 210 |
+
1,
|
| 211 |
+
"6xQl1pPk%9E~Znhm*:ng"
|
| 212 |
+
]
|
| 213 |
+
},
|
| 214 |
+
"fields": {},
|
| 215 |
+
"shadow": false,
|
| 216 |
+
"topLevel": true,
|
| 217 |
+
"x": 492,
|
| 218 |
+
"y": 463
|
| 219 |
+
},
|
| 220 |
+
"motion_pointtowards_menu": {
|
| 221 |
+
"opcode": "motion_pointtowards_menu",
|
| 222 |
+
"next": null,
|
| 223 |
+
"parent": "Ucm$YBs*^9GFTGXCbal@",
|
| 224 |
+
"inputs": {},
|
| 225 |
+
"fields": {
|
| 226 |
+
"TOWARDS": [
|
| 227 |
+
"_mouse_",
|
| 228 |
+
null
|
| 229 |
+
]
|
| 230 |
+
},
|
| 231 |
+
"shadow": true,
|
| 232 |
+
"topLevel": false
|
| 233 |
+
},
|
| 234 |
+
"motion_changexby": {
|
| 235 |
+
"opcode": "motion_changexby",
|
| 236 |
+
"next": null,
|
| 237 |
+
"parent": null,
|
| 238 |
+
"inputs": {
|
| 239 |
+
"DX": [
|
| 240 |
+
1,
|
| 241 |
+
[
|
| 242 |
+
4,
|
| 243 |
+
"10"
|
| 244 |
+
]
|
| 245 |
+
]
|
| 246 |
+
},
|
| 247 |
+
"fields": {},
|
| 248 |
+
"shadow": false,
|
| 249 |
+
"topLevel": true,
|
| 250 |
+
"x": 851,
|
| 251 |
+
"y": -409
|
| 252 |
+
},
|
| 253 |
+
"motion_setx": {
|
| 254 |
+
"opcode": "motion_setx",
|
| 255 |
+
"next": null,
|
| 256 |
+
"parent": null,
|
| 257 |
+
"inputs": {
|
| 258 |
+
"X": [
|
| 259 |
+
1,
|
| 260 |
+
[
|
| 261 |
+
4,
|
| 262 |
+
"0"
|
| 263 |
+
]
|
| 264 |
+
]
|
| 265 |
+
},
|
| 266 |
+
"fields": {},
|
| 267 |
+
"shadow": false,
|
| 268 |
+
"topLevel": true,
|
| 269 |
+
"x": 864,
|
| 270 |
+
"y": -194
|
| 271 |
+
},
|
| 272 |
+
"motion_changeyby": {
|
| 273 |
+
"opcode": "motion_changeyby",
|
| 274 |
+
"next": null,
|
| 275 |
+
"parent": null,
|
| 276 |
+
"inputs": {
|
| 277 |
+
"DY": [
|
| 278 |
+
1,
|
| 279 |
+
[
|
| 280 |
+
4,
|
| 281 |
+
"10"
|
| 282 |
+
]
|
| 283 |
+
]
|
| 284 |
+
},
|
| 285 |
+
"fields": {},
|
| 286 |
+
"shadow": false,
|
| 287 |
+
"topLevel": true,
|
| 288 |
+
"x": 861,
|
| 289 |
+
"y": -61
|
| 290 |
+
},
|
| 291 |
+
"motion_sety": {
|
| 292 |
+
"opcode": "motion_sety",
|
| 293 |
+
"next": null,
|
| 294 |
+
"parent": null,
|
| 295 |
+
"inputs": {
|
| 296 |
+
"Y": [
|
| 297 |
+
1,
|
| 298 |
+
[
|
| 299 |
+
4,
|
| 300 |
+
"0"
|
| 301 |
+
]
|
| 302 |
+
]
|
| 303 |
+
},
|
| 304 |
+
"fields": {},
|
| 305 |
+
"shadow": false,
|
| 306 |
+
"topLevel": true,
|
| 307 |
+
"x": 864,
|
| 308 |
+
"y": 66
|
| 309 |
+
},
|
| 310 |
+
"motion_ifonedgebounce": {
|
| 311 |
+
"opcode": "motion_ifonedgebounce",
|
| 312 |
+
"next": null,
|
| 313 |
+
"parent": null,
|
| 314 |
+
"inputs": {},
|
| 315 |
+
"fields": {},
|
| 316 |
+
"shadow": false,
|
| 317 |
+
"topLevel": true,
|
| 318 |
+
"x": 1131,
|
| 319 |
+
"y": -397
|
| 320 |
+
},
|
| 321 |
+
"motion_setrotationstyle": {
|
| 322 |
+
"opcode": "motion_setrotationstyle",
|
| 323 |
+
"next": null,
|
| 324 |
+
"parent": null,
|
| 325 |
+
"inputs": {},
|
| 326 |
+
"fields": {
|
| 327 |
+
"STYLE": [
|
| 328 |
+
"left-right",
|
| 329 |
+
null
|
| 330 |
+
]
|
| 331 |
+
},
|
| 332 |
+
"shadow": false,
|
| 333 |
+
"topLevel": true,
|
| 334 |
+
"x": 1128,
|
| 335 |
+
"y": -287
|
| 336 |
+
},
|
| 337 |
+
"motion_xposition": {
|
| 338 |
+
"opcode": "motion_xposition",
|
| 339 |
+
"next": null,
|
| 340 |
+
"parent": null,
|
| 341 |
+
"inputs": {},
|
| 342 |
+
"fields": {},
|
| 343 |
+
"shadow": false,
|
| 344 |
+
"topLevel": true,
|
| 345 |
+
"x": 1193,
|
| 346 |
+
"y": -136
|
| 347 |
+
},
|
| 348 |
+
"motion_yposition": {
|
| 349 |
+
"opcode": "motion_yposition",
|
| 350 |
+
"next": null,
|
| 351 |
+
"parent": null,
|
| 352 |
+
"inputs": {},
|
| 353 |
+
"fields": {},
|
| 354 |
+
"shadow": false,
|
| 355 |
+
"topLevel": true,
|
| 356 |
+
"x": 1181,
|
| 357 |
+
"y": -64
|
| 358 |
+
},
|
| 359 |
+
"motion_direction": {
|
| 360 |
+
"opcode": "motion_direction",
|
| 361 |
+
"next": null,
|
| 362 |
+
"parent": null,
|
| 363 |
+
"inputs": {},
|
| 364 |
+
"fields": {},
|
| 365 |
+
"shadow": false,
|
| 366 |
+
"topLevel": true,
|
| 367 |
+
"x": 1188,
|
| 368 |
+
"y": 21
|
| 369 |
+
},
|
| 370 |
+
"control_wait": {
|
| 371 |
+
"opcode": "control_wait",
|
| 372 |
+
"next": null,
|
| 373 |
+
"parent": null,
|
| 374 |
+
"inputs": {
|
| 375 |
+
"DURATION": [
|
| 376 |
+
1,
|
| 377 |
+
[
|
| 378 |
+
5,
|
| 379 |
+
"1"
|
| 380 |
+
]
|
| 381 |
+
]
|
| 382 |
+
},
|
| 383 |
+
"fields": {},
|
| 384 |
+
"shadow": false,
|
| 385 |
+
"topLevel": true,
|
| 386 |
+
"x": 337,
|
| 387 |
+
"y": 129
|
| 388 |
+
},
|
| 389 |
+
"control_repeat": {
|
| 390 |
+
"opcode": "control_repeat",
|
| 391 |
+
"next": null,
|
| 392 |
+
"parent": null,
|
| 393 |
+
"inputs": {
|
| 394 |
+
"TIMES": [
|
| 395 |
+
1,
|
| 396 |
+
[
|
| 397 |
+
6,
|
| 398 |
+
"10"
|
| 399 |
+
]
|
| 400 |
+
]
|
| 401 |
+
},
|
| 402 |
+
"fields": {},
|
| 403 |
+
"shadow": false,
|
| 404 |
+
"topLevel": true,
|
| 405 |
+
"x": 348,
|
| 406 |
+
"y": 265
|
| 407 |
+
},
|
| 408 |
+
"control_forever": {
|
| 409 |
+
"opcode": "control_forever",
|
| 410 |
+
"next": null,
|
| 411 |
+
"parent": null,
|
| 412 |
+
"inputs": {},
|
| 413 |
+
"fields": {},
|
| 414 |
+
"shadow": false,
|
| 415 |
+
"topLevel": true,
|
| 416 |
+
"x": 334,
|
| 417 |
+
"y": 439
|
| 418 |
+
},
|
| 419 |
+
"control_if": {
|
| 420 |
+
"opcode": "control_if",
|
| 421 |
+
"next": null,
|
| 422 |
+
"parent": null,
|
| 423 |
+
"inputs": {},
|
| 424 |
+
"fields": {},
|
| 425 |
+
"shadow": false,
|
| 426 |
+
"topLevel": true,
|
| 427 |
+
"x": 331,
|
| 428 |
+
"y": 597
|
| 429 |
+
},
|
| 430 |
+
"control_if_else": {
|
| 431 |
+
"opcode": "control_if_else",
|
| 432 |
+
"next": null,
|
| 433 |
+
"parent": null,
|
| 434 |
+
"inputs": {},
|
| 435 |
+
"fields": {},
|
| 436 |
+
"shadow": false,
|
| 437 |
+
"topLevel": true,
|
| 438 |
+
"x": 335,
|
| 439 |
+
"y": 779
|
| 440 |
+
},
|
| 441 |
+
"control_wait_until": {
|
| 442 |
+
"opcode": "control_wait_until",
|
| 443 |
+
"next": null,
|
| 444 |
+
"parent": null,
|
| 445 |
+
"inputs": {},
|
| 446 |
+
"fields": {},
|
| 447 |
+
"shadow": false,
|
| 448 |
+
"topLevel": true,
|
| 449 |
+
"x": 676,
|
| 450 |
+
"y": 285
|
| 451 |
+
},
|
| 452 |
+
"control_repeat_until": {
|
| 453 |
+
"opcode": "control_repeat_until",
|
| 454 |
+
"next": null,
|
| 455 |
+
"parent": null,
|
| 456 |
+
"inputs": {},
|
| 457 |
+
"fields": {},
|
| 458 |
+
"shadow": false,
|
| 459 |
+
"topLevel": true,
|
| 460 |
+
"x": 692,
|
| 461 |
+
"y": 381
|
| 462 |
+
},
|
| 463 |
+
"control_stop": {
|
| 464 |
+
"opcode": "control_stop",
|
| 465 |
+
"next": null,
|
| 466 |
+
"parent": null,
|
| 467 |
+
"inputs": {},
|
| 468 |
+
"fields": {
|
| 469 |
+
"STOP_OPTION": [
|
| 470 |
+
"all",
|
| 471 |
+
null
|
| 472 |
+
]
|
| 473 |
+
},
|
| 474 |
+
"shadow": false,
|
| 475 |
+
"topLevel": true,
|
| 476 |
+
"x": 708,
|
| 477 |
+
"y": 545,
|
| 478 |
+
"mutation": {
|
| 479 |
+
"tagName": "mutation",
|
| 480 |
+
"children": [],
|
| 481 |
+
"hasnext": "false"
|
| 482 |
+
}
|
| 483 |
+
},
|
| 484 |
+
"control_start_as_clone": {
|
| 485 |
+
"opcode": "control_start_as_clone",
|
| 486 |
+
"next": null,
|
| 487 |
+
"parent": null,
|
| 488 |
+
"inputs": {},
|
| 489 |
+
"fields": {},
|
| 490 |
+
"shadow": false,
|
| 491 |
+
"topLevel": true,
|
| 492 |
+
"x": 665,
|
| 493 |
+
"y": 672
|
| 494 |
+
},
|
| 495 |
+
"control_create_clone_of": {
|
| 496 |
+
"opcode": "control_create_clone_of",
|
| 497 |
+
"next": null,
|
| 498 |
+
"parent": null,
|
| 499 |
+
"inputs": {
|
| 500 |
+
"CLONE_OPTION": [
|
| 501 |
+
1,
|
| 502 |
+
"t))DW9(QSKB]3C/3Ou+J"
|
| 503 |
+
]
|
| 504 |
+
},
|
| 505 |
+
"fields": {},
|
| 506 |
+
"shadow": false,
|
| 507 |
+
"topLevel": true,
|
| 508 |
+
"x": 648,
|
| 509 |
+
"y": 797
|
| 510 |
+
},
|
| 511 |
+
"control_create_clone_of_menu": {
|
| 512 |
+
"opcode": "control_create_clone_of_menu",
|
| 513 |
+
"next": null,
|
| 514 |
+
"parent": "80yo/}Cw++Z.;x[ohh|7",
|
| 515 |
+
"inputs": {},
|
| 516 |
+
"fields": {
|
| 517 |
+
"CLONE_OPTION": [
|
| 518 |
+
"_myself_",
|
| 519 |
+
null
|
| 520 |
+
]
|
| 521 |
+
},
|
| 522 |
+
"shadow": true,
|
| 523 |
+
"topLevel": false
|
| 524 |
+
},
|
| 525 |
+
"control_delete_this_clone": {
|
| 526 |
+
"opcode": "control_delete_this_clone",
|
| 527 |
+
"next": null,
|
| 528 |
+
"parent": null,
|
| 529 |
+
"inputs": {},
|
| 530 |
+
"fields": {},
|
| 531 |
+
"shadow": false,
|
| 532 |
+
"topLevel": true,
|
| 533 |
+
"x": 642,
|
| 534 |
+
"y": 914
|
| 535 |
+
},
|
| 536 |
+
"event_whenflagclicked": {
|
| 537 |
+
"opcode": "event_whenflagclicked",
|
| 538 |
+
"next": null,
|
| 539 |
+
"parent": null,
|
| 540 |
+
"inputs": {},
|
| 541 |
+
"fields": {},
|
| 542 |
+
"shadow": false,
|
| 543 |
+
"topLevel": true,
|
| 544 |
+
"x": 166,
|
| 545 |
+
"y": -422
|
| 546 |
+
},
|
| 547 |
+
"event_whenkeypressed": {
|
| 548 |
+
"opcode": "event_whenkeypressed",
|
| 549 |
+
"next": null,
|
| 550 |
+
"parent": null,
|
| 551 |
+
"inputs": {},
|
| 552 |
+
"fields": {
|
| 553 |
+
"KEY_OPTION": [
|
| 554 |
+
"space",
|
| 555 |
+
null
|
| 556 |
+
]
|
| 557 |
+
},
|
| 558 |
+
"shadow": false,
|
| 559 |
+
"topLevel": true,
|
| 560 |
+
"x": 151,
|
| 561 |
+
"y": -329
|
| 562 |
+
},
|
| 563 |
+
"event_whenthisspriteclicked": {
|
| 564 |
+
"opcode": "event_whenthisspriteclicked",
|
| 565 |
+
"next": null,
|
| 566 |
+
"parent": null,
|
| 567 |
+
"inputs": {},
|
| 568 |
+
"fields": {},
|
| 569 |
+
"shadow": false,
|
| 570 |
+
"topLevel": true,
|
| 571 |
+
"x": 156,
|
| 572 |
+
"y": -223
|
| 573 |
+
},
|
| 574 |
+
"event_whenbackdropswitchesto": {
|
| 575 |
+
"opcode": "event_whenbackdropswitchesto",
|
| 576 |
+
"next": null,
|
| 577 |
+
"parent": null,
|
| 578 |
+
"inputs": {},
|
| 579 |
+
"fields": {
|
| 580 |
+
"BACKDROP": [
|
| 581 |
+
"backdrop1",
|
| 582 |
+
null
|
| 583 |
+
]
|
| 584 |
+
},
|
| 585 |
+
"shadow": false,
|
| 586 |
+
"topLevel": true,
|
| 587 |
+
"x": 148,
|
| 588 |
+
"y": -101
|
| 589 |
+
},
|
| 590 |
+
"event_whengreaterthan": {
|
| 591 |
+
"opcode": "event_whengreaterthan",
|
| 592 |
+
"next": null,
|
| 593 |
+
"parent": null,
|
| 594 |
+
"inputs": {
|
| 595 |
+
"VALUE": [
|
| 596 |
+
1,
|
| 597 |
+
[
|
| 598 |
+
4,
|
| 599 |
+
"10"
|
| 600 |
+
]
|
| 601 |
+
]
|
| 602 |
+
},
|
| 603 |
+
"fields": {
|
| 604 |
+
"WHENGREATERTHANMENU": [
|
| 605 |
+
"LOUDNESS",
|
| 606 |
+
null
|
| 607 |
+
]
|
| 608 |
+
},
|
| 609 |
+
"shadow": false,
|
| 610 |
+
"topLevel": true,
|
| 611 |
+
"x": 150,
|
| 612 |
+
"y": 10
|
| 613 |
+
},
|
| 614 |
+
"event_whenbroadcastreceived": {
|
| 615 |
+
"opcode": "event_whenbroadcastreceived",
|
| 616 |
+
"next": null,
|
| 617 |
+
"parent": null,
|
| 618 |
+
"inputs": {},
|
| 619 |
+
"fields": {
|
| 620 |
+
"BROADCAST_OPTION": [
|
| 621 |
+
"message1",
|
| 622 |
+
"5O!nei;S$!c!=hCT}0:a"
|
| 623 |
+
]
|
| 624 |
+
},
|
| 625 |
+
"shadow": false,
|
| 626 |
+
"topLevel": true,
|
| 627 |
+
"x": 141,
|
| 628 |
+
"y": 118
|
| 629 |
+
},
|
| 630 |
+
"event_broadcast": {
|
| 631 |
+
"opcode": "event_broadcast",
|
| 632 |
+
"next": null,
|
| 633 |
+
"parent": null,
|
| 634 |
+
"inputs": {
|
| 635 |
+
"BROADCAST_INPUT": [
|
| 636 |
+
1,
|
| 637 |
+
[
|
| 638 |
+
11,
|
| 639 |
+
"message1",
|
| 640 |
+
"5O!nei;S$!c!=hCT}0:a"
|
| 641 |
+
]
|
| 642 |
+
]
|
| 643 |
+
},
|
| 644 |
+
"fields": {},
|
| 645 |
+
"shadow": false,
|
| 646 |
+
"topLevel": true,
|
| 647 |
+
"x": 151,
|
| 648 |
+
"y": 229
|
| 649 |
+
},
|
| 650 |
+
"event_broadcastandwait": {
|
| 651 |
+
"opcode": "event_broadcastandwait",
|
| 652 |
+
"next": null,
|
| 653 |
+
"parent": null,
|
| 654 |
+
"inputs": {
|
| 655 |
+
"BROADCAST_INPUT": [
|
| 656 |
+
1,
|
| 657 |
+
[
|
| 658 |
+
11,
|
| 659 |
+
"message1",
|
| 660 |
+
"5O!nei;S$!c!=hCT}0:a"
|
| 661 |
+
]
|
| 662 |
+
]
|
| 663 |
+
},
|
| 664 |
+
"fields": {},
|
| 665 |
+
"shadow": false,
|
| 666 |
+
"topLevel": true,
|
| 667 |
+
"x": 157,
|
| 668 |
+
"y": 340
|
| 669 |
+
},
|
| 670 |
+
"sensing_touchingobject": {
|
| 671 |
+
"opcode": "sensing_touchingobject",
|
| 672 |
+
"next": null,
|
| 673 |
+
"parent": null,
|
| 674 |
+
"inputs": {
|
| 675 |
+
"TOUCHINGOBJECTMENU": [
|
| 676 |
+
1,
|
| 677 |
+
"xSKW9a+wTnM~h~So8Jc]"
|
| 678 |
+
]
|
| 679 |
+
},
|
| 680 |
+
"fields": {},
|
| 681 |
+
"shadow": false,
|
| 682 |
+
"topLevel": true,
|
| 683 |
+
"x": 359,
|
| 684 |
+
"y": 116
|
| 685 |
+
},
|
| 686 |
+
"sensing_touchingobjectmenu": {
|
| 687 |
+
"opcode": "sensing_touchingobjectmenu",
|
| 688 |
+
"next": null,
|
| 689 |
+
"parent": "Y(n,F@BYzwd4CiN|Bh[P",
|
| 690 |
+
"inputs": {},
|
| 691 |
+
"fields": {
|
| 692 |
+
"TOUCHINGOBJECTMENU": [
|
| 693 |
+
"_mouse_",
|
| 694 |
+
null
|
| 695 |
+
]
|
| 696 |
+
},
|
| 697 |
+
"shadow": true,
|
| 698 |
+
"topLevel": false
|
| 699 |
+
},
|
| 700 |
+
"sensing_touchingcolor": {
|
| 701 |
+
"opcode": "sensing_touchingcolor",
|
| 702 |
+
"next": null,
|
| 703 |
+
"parent": null,
|
| 704 |
+
"inputs": {
|
| 705 |
+
"COLOR": [
|
| 706 |
+
1,
|
| 707 |
+
[
|
| 708 |
+
9,
|
| 709 |
+
"#55b888"
|
| 710 |
+
]
|
| 711 |
+
]
|
| 712 |
+
},
|
| 713 |
+
"fields": {},
|
| 714 |
+
"shadow": false,
|
| 715 |
+
"topLevel": true,
|
| 716 |
+
"x": 360,
|
| 717 |
+
"y": 188
|
| 718 |
+
},
|
| 719 |
+
"sensing_coloristouchingcolor": {
|
| 720 |
+
"opcode": "sensing_coloristouchingcolor",
|
| 721 |
+
"next": null,
|
| 722 |
+
"parent": null,
|
| 723 |
+
"inputs": {
|
| 724 |
+
"COLOR": [
|
| 725 |
+
1,
|
| 726 |
+
[
|
| 727 |
+
9,
|
| 728 |
+
"#d019f2"
|
| 729 |
+
]
|
| 730 |
+
],
|
| 731 |
+
"COLOR2": [
|
| 732 |
+
1,
|
| 733 |
+
[
|
| 734 |
+
9,
|
| 735 |
+
"#2b0de3"
|
| 736 |
+
]
|
| 737 |
+
]
|
| 738 |
+
},
|
| 739 |
+
"fields": {},
|
| 740 |
+
"shadow": false,
|
| 741 |
+
"topLevel": true,
|
| 742 |
+
"x": 348,
|
| 743 |
+
"y": 277
|
| 744 |
+
},
|
| 745 |
+
"sensing_askandwait": {
|
| 746 |
+
"opcode": "sensing_askandwait",
|
| 747 |
+
"next": null,
|
| 748 |
+
"parent": null,
|
| 749 |
+
"inputs": {
|
| 750 |
+
"QUESTION": [
|
| 751 |
+
1,
|
| 752 |
+
[
|
| 753 |
+
10,
|
| 754 |
+
"What's your name?"
|
| 755 |
+
]
|
| 756 |
+
]
|
| 757 |
+
},
|
| 758 |
+
"fields": {},
|
| 759 |
+
"shadow": false,
|
| 760 |
+
"topLevel": true,
|
| 761 |
+
"x": 338,
|
| 762 |
+
"y": 354
|
| 763 |
+
},
|
| 764 |
+
"sensing_answer": {
|
| 765 |
+
"opcode": "sensing_answer",
|
| 766 |
+
"next": null,
|
| 767 |
+
"parent": null,
|
| 768 |
+
"inputs": {},
|
| 769 |
+
"fields": {},
|
| 770 |
+
"shadow": false,
|
| 771 |
+
"topLevel": true,
|
| 772 |
+
"x": 782,
|
| 773 |
+
"y": 111
|
| 774 |
+
},
|
| 775 |
+
"sensing_keypressed": {
|
| 776 |
+
"opcode": "sensing_keypressed",
|
| 777 |
+
"next": null,
|
| 778 |
+
"parent": null,
|
| 779 |
+
"inputs": {
|
| 780 |
+
"KEY_OPTION": [
|
| 781 |
+
1,
|
| 782 |
+
"SNlf@Im$sv%.6ULi-f3i"
|
| 783 |
+
]
|
| 784 |
+
},
|
| 785 |
+
"fields": {},
|
| 786 |
+
"shadow": false,
|
| 787 |
+
"topLevel": true,
|
| 788 |
+
"x": 762,
|
| 789 |
+
"y": 207
|
| 790 |
+
},
|
| 791 |
+
"sensing_keyoptions": {
|
| 792 |
+
"opcode": "sensing_keyoptions",
|
| 793 |
+
"next": null,
|
| 794 |
+
"parent": "7$xEUO.2hH2R6vh!$(Uj",
|
| 795 |
+
"inputs": {},
|
| 796 |
+
"fields": {
|
| 797 |
+
"KEY_OPTION": [
|
| 798 |
+
"space",
|
| 799 |
+
null
|
| 800 |
+
]
|
| 801 |
+
},
|
| 802 |
+
"shadow": true,
|
| 803 |
+
"topLevel": false
|
| 804 |
+
},
|
| 805 |
+
"sensing_mousedown": {
|
| 806 |
+
"opcode": "sensing_mousedown",
|
| 807 |
+
"next": null,
|
| 808 |
+
"parent": null,
|
| 809 |
+
"inputs": {},
|
| 810 |
+
"fields": {},
|
| 811 |
+
"shadow": false,
|
| 812 |
+
"topLevel": true,
|
| 813 |
+
"x": 822,
|
| 814 |
+
"y": 422
|
| 815 |
+
},
|
| 816 |
+
"sensing_mousex": {
|
| 817 |
+
"opcode": "sensing_mousex",
|
| 818 |
+
"next": null,
|
| 819 |
+
"parent": null,
|
| 820 |
+
"inputs": {},
|
| 821 |
+
"fields": {},
|
| 822 |
+
"shadow": false,
|
| 823 |
+
"topLevel": true,
|
| 824 |
+
"x": 302,
|
| 825 |
+
"y": 528
|
| 826 |
+
},
|
| 827 |
+
"sensing_mousey": {
|
| 828 |
+
"opcode": "sensing_mousey",
|
| 829 |
+
"next": null,
|
| 830 |
+
"parent": null,
|
| 831 |
+
"inputs": {},
|
| 832 |
+
"fields": {},
|
| 833 |
+
"shadow": false,
|
| 834 |
+
"topLevel": true,
|
| 835 |
+
"x": 668,
|
| 836 |
+
"y": 547
|
| 837 |
+
},
|
| 838 |
+
"sensing_setdragmode": {
|
| 839 |
+
"opcode": "sensing_setdragmode",
|
| 840 |
+
"next": null,
|
| 841 |
+
"parent": null,
|
| 842 |
+
"inputs": {},
|
| 843 |
+
"fields": {
|
| 844 |
+
"DRAG_MODE": [
|
| 845 |
+
"draggable",
|
| 846 |
+
null
|
| 847 |
+
]
|
| 848 |
+
},
|
| 849 |
+
"shadow": false,
|
| 850 |
+
"topLevel": true,
|
| 851 |
+
"x": 950,
|
| 852 |
+
"y": 574
|
| 853 |
+
},
|
| 854 |
+
"sensing_loudness": {
|
| 855 |
+
"opcode": "sensing_loudness",
|
| 856 |
+
"next": null,
|
| 857 |
+
"parent": null,
|
| 858 |
+
"inputs": {},
|
| 859 |
+
"fields": {},
|
| 860 |
+
"shadow": false,
|
| 861 |
+
"topLevel": true,
|
| 862 |
+
"x": 658,
|
| 863 |
+
"y": 703
|
| 864 |
+
},
|
| 865 |
+
"sensing_timer": {
|
| 866 |
+
"opcode": "sensing_timer",
|
| 867 |
+
"next": null,
|
| 868 |
+
"parent": null,
|
| 869 |
+
"inputs": {},
|
| 870 |
+
"fields": {},
|
| 871 |
+
"shadow": false,
|
| 872 |
+
"topLevel": true,
|
| 873 |
+
"x": 459,
|
| 874 |
+
"y": 671
|
| 875 |
+
},
|
| 876 |
+
"sensing_resettimer": {
|
| 877 |
+
"opcode": "sensing_resettimer",
|
| 878 |
+
"next": null,
|
| 879 |
+
"parent": null,
|
| 880 |
+
"inputs": {},
|
| 881 |
+
"fields": {},
|
| 882 |
+
"shadow": false,
|
| 883 |
+
"topLevel": true,
|
| 884 |
+
"x": 462,
|
| 885 |
+
"y": 781
|
| 886 |
+
},
|
| 887 |
+
"sensing_of": {
|
| 888 |
+
"opcode": "sensing_of",
|
| 889 |
+
"next": null,
|
| 890 |
+
"parent": null,
|
| 891 |
+
"inputs": {
|
| 892 |
+
"OBJECT": [
|
| 893 |
+
1,
|
| 894 |
+
"t+o*y;iz,!O#aT|qM_+O"
|
| 895 |
+
]
|
| 896 |
+
},
|
| 897 |
+
"fields": {
|
| 898 |
+
"PROPERTY": [
|
| 899 |
+
"backdrop #",
|
| 900 |
+
null
|
| 901 |
+
]
|
| 902 |
+
},
|
| 903 |
+
"shadow": false,
|
| 904 |
+
"topLevel": true,
|
| 905 |
+
"x": 997,
|
| 906 |
+
"y": 754
|
| 907 |
+
},
|
| 908 |
+
"sensing_of_object_menu": {
|
| 909 |
+
"opcode": "sensing_of_object_menu",
|
| 910 |
+
"next": null,
|
| 911 |
+
"parent": "[4I2wIG/tNc@LQ-;FbsB",
|
| 912 |
+
"inputs": {},
|
| 913 |
+
"fields": {
|
| 914 |
+
"OBJECT": [
|
| 915 |
+
"_stage_",
|
| 916 |
+
null
|
| 917 |
+
]
|
| 918 |
+
},
|
| 919 |
+
"shadow": true,
|
| 920 |
+
"topLevel": false
|
| 921 |
+
},
|
| 922 |
+
"sensing_current": {
|
| 923 |
+
"opcode": "sensing_current",
|
| 924 |
+
"next": null,
|
| 925 |
+
"parent": null,
|
| 926 |
+
"inputs": {},
|
| 927 |
+
"fields": {
|
| 928 |
+
"CURRENTMENU": [
|
| 929 |
+
"YEAR",
|
| 930 |
+
null
|
| 931 |
+
]
|
| 932 |
+
},
|
| 933 |
+
"shadow": false,
|
| 934 |
+
"topLevel": true,
|
| 935 |
+
"x": 627,
|
| 936 |
+
"y": 884
|
| 937 |
+
},
|
| 938 |
+
"sensing_dayssince2000": {
|
| 939 |
+
"opcode": "sensing_dayssince2000",
|
| 940 |
+
"next": null,
|
| 941 |
+
"parent": null,
|
| 942 |
+
"inputs": {},
|
| 943 |
+
"fields": {},
|
| 944 |
+
"shadow": false,
|
| 945 |
+
"topLevel": true,
|
| 946 |
+
"x": 959,
|
| 947 |
+
"y": 903
|
| 948 |
+
},
|
| 949 |
+
"sensing_username": {
|
| 950 |
+
"opcode": "sensing_username",
|
| 951 |
+
"next": null,
|
| 952 |
+
"parent": null,
|
| 953 |
+
"inputs": {},
|
| 954 |
+
"fields": {},
|
| 955 |
+
"shadow": false,
|
| 956 |
+
"topLevel": true,
|
| 957 |
+
"x": 833,
|
| 958 |
+
"y": 757
|
| 959 |
+
},
|
| 960 |
+
"operator_add": {
|
| 961 |
+
"opcode": "operator_add",
|
| 962 |
+
"next": null,
|
| 963 |
+
"parent": null,
|
| 964 |
+
"inputs": {
|
| 965 |
+
"NUM1": [
|
| 966 |
+
1,
|
| 967 |
+
[
|
| 968 |
+
4,
|
| 969 |
+
""
|
| 970 |
+
]
|
| 971 |
+
],
|
| 972 |
+
"NUM2": [
|
| 973 |
+
1,
|
| 974 |
+
[
|
| 975 |
+
4,
|
| 976 |
+
""
|
| 977 |
+
]
|
| 978 |
+
]
|
| 979 |
+
},
|
| 980 |
+
"fields": {},
|
| 981 |
+
"shadow": false,
|
| 982 |
+
"topLevel": true,
|
| 983 |
+
"x": 128,
|
| 984 |
+
"y": 153
|
| 985 |
+
},
|
| 986 |
+
"operator_subtract": {
|
| 987 |
+
"opcode": "operator_subtract",
|
| 988 |
+
"next": null,
|
| 989 |
+
"parent": null,
|
| 990 |
+
"inputs": {
|
| 991 |
+
"NUM1": [
|
| 992 |
+
1,
|
| 993 |
+
[
|
| 994 |
+
4,
|
| 995 |
+
""
|
| 996 |
+
]
|
| 997 |
+
],
|
| 998 |
+
"NUM2": [
|
| 999 |
+
1,
|
| 1000 |
+
[
|
| 1001 |
+
4,
|
| 1002 |
+
""
|
| 1003 |
+
]
|
| 1004 |
+
]
|
| 1005 |
+
},
|
| 1006 |
+
"fields": {},
|
| 1007 |
+
"shadow": false,
|
| 1008 |
+
"topLevel": true,
|
| 1009 |
+
"x": 134,
|
| 1010 |
+
"y": 214
|
| 1011 |
+
},
|
| 1012 |
+
"operator_multiply": {
|
| 1013 |
+
"opcode": "operator_multiply",
|
| 1014 |
+
"next": null,
|
| 1015 |
+
"parent": null,
|
| 1016 |
+
"inputs": {
|
| 1017 |
+
"NUM1": [
|
| 1018 |
+
1,
|
| 1019 |
+
[
|
| 1020 |
+
4,
|
| 1021 |
+
""
|
| 1022 |
+
]
|
| 1023 |
+
],
|
| 1024 |
+
"NUM2": [
|
| 1025 |
+
1,
|
| 1026 |
+
[
|
| 1027 |
+
4,
|
| 1028 |
+
""
|
| 1029 |
+
]
|
| 1030 |
+
]
|
| 1031 |
+
},
|
| 1032 |
+
"fields": {},
|
| 1033 |
+
"shadow": false,
|
| 1034 |
+
"topLevel": true,
|
| 1035 |
+
"x": 134,
|
| 1036 |
+
"y": 278
|
| 1037 |
+
},
|
| 1038 |
+
"operator_divide": {
|
| 1039 |
+
"opcode": "operator_divide",
|
| 1040 |
+
"next": null,
|
| 1041 |
+
"parent": null,
|
| 1042 |
+
"inputs": {
|
| 1043 |
+
"NUM1": [
|
| 1044 |
+
1,
|
| 1045 |
+
[
|
| 1046 |
+
4,
|
| 1047 |
+
""
|
| 1048 |
+
]
|
| 1049 |
+
],
|
| 1050 |
+
"NUM2": [
|
| 1051 |
+
1,
|
| 1052 |
+
[
|
| 1053 |
+
4,
|
| 1054 |
+
""
|
| 1055 |
+
]
|
| 1056 |
+
]
|
| 1057 |
+
},
|
| 1058 |
+
"fields": {},
|
| 1059 |
+
"shadow": false,
|
| 1060 |
+
"topLevel": true,
|
| 1061 |
+
"x": 138,
|
| 1062 |
+
"y": 359
|
| 1063 |
+
},
|
| 1064 |
+
"operator_random": {
|
| 1065 |
+
"opcode": "operator_random",
|
| 1066 |
+
"next": null,
|
| 1067 |
+
"parent": null,
|
| 1068 |
+
"inputs": {
|
| 1069 |
+
"FROM": [
|
| 1070 |
+
1,
|
| 1071 |
+
[
|
| 1072 |
+
4,
|
| 1073 |
+
"1"
|
| 1074 |
+
]
|
| 1075 |
+
],
|
| 1076 |
+
"TO": [
|
| 1077 |
+
1,
|
| 1078 |
+
[
|
| 1079 |
+
4,
|
| 1080 |
+
"10"
|
| 1081 |
+
]
|
| 1082 |
+
]
|
| 1083 |
+
},
|
| 1084 |
+
"fields": {},
|
| 1085 |
+
"shadow": false,
|
| 1086 |
+
"topLevel": true,
|
| 1087 |
+
"x": 311,
|
| 1088 |
+
"y": 157
|
| 1089 |
+
},
|
| 1090 |
+
"operator_gt": {
|
| 1091 |
+
"opcode": "operator_gt",
|
| 1092 |
+
"next": null,
|
| 1093 |
+
"parent": null,
|
| 1094 |
+
"inputs": {
|
| 1095 |
+
"OPERAND1": [
|
| 1096 |
+
1,
|
| 1097 |
+
[
|
| 1098 |
+
10,
|
| 1099 |
+
""
|
| 1100 |
+
]
|
| 1101 |
+
],
|
| 1102 |
+
"OPERAND2": [
|
| 1103 |
+
1,
|
| 1104 |
+
[
|
| 1105 |
+
10,
|
| 1106 |
+
"50"
|
| 1107 |
+
]
|
| 1108 |
+
]
|
| 1109 |
+
},
|
| 1110 |
+
"fields": {},
|
| 1111 |
+
"shadow": false,
|
| 1112 |
+
"topLevel": true,
|
| 1113 |
+
"x": 348,
|
| 1114 |
+
"y": 217
|
| 1115 |
+
},
|
| 1116 |
+
"operator_lt": {
|
| 1117 |
+
"opcode": "operator_lt",
|
| 1118 |
+
"next": null,
|
| 1119 |
+
"parent": null,
|
| 1120 |
+
"inputs": {
|
| 1121 |
+
"OPERAND1": [
|
| 1122 |
+
1,
|
| 1123 |
+
[
|
| 1124 |
+
10,
|
| 1125 |
+
""
|
| 1126 |
+
]
|
| 1127 |
+
],
|
| 1128 |
+
"OPERAND2": [
|
| 1129 |
+
1,
|
| 1130 |
+
[
|
| 1131 |
+
10,
|
| 1132 |
+
"50"
|
| 1133 |
+
]
|
| 1134 |
+
]
|
| 1135 |
+
},
|
| 1136 |
+
"fields": {},
|
| 1137 |
+
"shadow": false,
|
| 1138 |
+
"topLevel": true,
|
| 1139 |
+
"x": 345,
|
| 1140 |
+
"y": 286
|
| 1141 |
+
},
|
| 1142 |
+
"operator_equals": {
|
| 1143 |
+
"opcode": "operator_equals",
|
| 1144 |
+
"next": null,
|
| 1145 |
+
"parent": null,
|
| 1146 |
+
"inputs": {
|
| 1147 |
+
"OPERAND1": [
|
| 1148 |
+
1,
|
| 1149 |
+
[
|
| 1150 |
+
10,
|
| 1151 |
+
""
|
| 1152 |
+
]
|
| 1153 |
+
],
|
| 1154 |
+
"OPERAND2": [
|
| 1155 |
+
1,
|
| 1156 |
+
[
|
| 1157 |
+
10,
|
| 1158 |
+
"50"
|
| 1159 |
+
]
|
| 1160 |
+
]
|
| 1161 |
+
},
|
| 1162 |
+
"fields": {},
|
| 1163 |
+
"shadow": false,
|
| 1164 |
+
"topLevel": true,
|
| 1165 |
+
"x": 345,
|
| 1166 |
+
"y": 372
|
| 1167 |
+
},
|
| 1168 |
+
"operator_and": {
|
| 1169 |
+
"opcode": "operator_and",
|
| 1170 |
+
"next": null,
|
| 1171 |
+
"parent": null,
|
| 1172 |
+
"inputs": {},
|
| 1173 |
+
"fields": {},
|
| 1174 |
+
"shadow": false,
|
| 1175 |
+
"topLevel": true,
|
| 1176 |
+
"x": 701,
|
| 1177 |
+
"y": 158
|
| 1178 |
+
},
|
| 1179 |
+
"operator_or": {
|
| 1180 |
+
"opcode": "operator_or",
|
| 1181 |
+
"next": null,
|
| 1182 |
+
"parent": null,
|
| 1183 |
+
"inputs": {},
|
| 1184 |
+
"fields": {},
|
| 1185 |
+
"shadow": false,
|
| 1186 |
+
"topLevel": true,
|
| 1187 |
+
"x": 705,
|
| 1188 |
+
"y": 222
|
| 1189 |
+
},
|
| 1190 |
+
"operator_not": {
|
| 1191 |
+
"opcode": "operator_not",
|
| 1192 |
+
"next": null,
|
| 1193 |
+
"parent": null,
|
| 1194 |
+
"inputs": {},
|
| 1195 |
+
"fields": {},
|
| 1196 |
+
"shadow": false,
|
| 1197 |
+
"topLevel": true,
|
| 1198 |
+
"x": 734,
|
| 1199 |
+
"y": 283
|
| 1200 |
+
},
|
| 1201 |
+
"operator_join": {
|
| 1202 |
+
"opcode": "operator_join",
|
| 1203 |
+
"next": null,
|
| 1204 |
+
"parent": null,
|
| 1205 |
+
"inputs": {
|
| 1206 |
+
"STRING1": [
|
| 1207 |
+
1,
|
| 1208 |
+
[
|
| 1209 |
+
10,
|
| 1210 |
+
"apple "
|
| 1211 |
+
]
|
| 1212 |
+
],
|
| 1213 |
+
"STRING2": [
|
| 1214 |
+
1,
|
| 1215 |
+
[
|
| 1216 |
+
10,
|
| 1217 |
+
"banana"
|
| 1218 |
+
]
|
| 1219 |
+
]
|
| 1220 |
+
},
|
| 1221 |
+
"fields": {},
|
| 1222 |
+
"shadow": false,
|
| 1223 |
+
"topLevel": true,
|
| 1224 |
+
"x": 663,
|
| 1225 |
+
"y": 378
|
| 1226 |
+
},
|
| 1227 |
+
"operator_letter_of": {
|
| 1228 |
+
"opcode": "operator_letter_of",
|
| 1229 |
+
"next": null,
|
| 1230 |
+
"parent": null,
|
| 1231 |
+
"inputs": {
|
| 1232 |
+
"LETTER": [
|
| 1233 |
+
1,
|
| 1234 |
+
[
|
| 1235 |
+
6,
|
| 1236 |
+
"1"
|
| 1237 |
+
]
|
| 1238 |
+
],
|
| 1239 |
+
"STRING": [
|
| 1240 |
+
1,
|
| 1241 |
+
[
|
| 1242 |
+
10,
|
| 1243 |
+
"apple"
|
| 1244 |
+
]
|
| 1245 |
+
]
|
| 1246 |
+
},
|
| 1247 |
+
"fields": {},
|
| 1248 |
+
"shadow": false,
|
| 1249 |
+
"topLevel": true,
|
| 1250 |
+
"x": 664,
|
| 1251 |
+
"y": 445
|
| 1252 |
+
},
|
| 1253 |
+
"operator_length": {
|
| 1254 |
+
"opcode": "operator_length",
|
| 1255 |
+
"next": null,
|
| 1256 |
+
"parent": null,
|
| 1257 |
+
"inputs": {
|
| 1258 |
+
"STRING": [
|
| 1259 |
+
1,
|
| 1260 |
+
[
|
| 1261 |
+
10,
|
| 1262 |
+
"apple"
|
| 1263 |
+
]
|
| 1264 |
+
]
|
| 1265 |
+
},
|
| 1266 |
+
"fields": {},
|
| 1267 |
+
"shadow": false,
|
| 1268 |
+
"topLevel": true,
|
| 1269 |
+
"x": 664,
|
| 1270 |
+
"y": 521
|
| 1271 |
+
},
|
| 1272 |
+
"operator_contains": {
|
| 1273 |
+
"opcode": "operator_contains",
|
| 1274 |
+
"next": null,
|
| 1275 |
+
"parent": null,
|
| 1276 |
+
"inputs": {
|
| 1277 |
+
"STRING1": [
|
| 1278 |
+
1,
|
| 1279 |
+
[
|
| 1280 |
+
10,
|
| 1281 |
+
"apple"
|
| 1282 |
+
]
|
| 1283 |
+
],
|
| 1284 |
+
"STRING2": [
|
| 1285 |
+
1,
|
| 1286 |
+
[
|
| 1287 |
+
10,
|
| 1288 |
+
"a"
|
| 1289 |
+
]
|
| 1290 |
+
]
|
| 1291 |
+
},
|
| 1292 |
+
"fields": {},
|
| 1293 |
+
"shadow": false,
|
| 1294 |
+
"topLevel": true,
|
| 1295 |
+
"x": 634,
|
| 1296 |
+
"y": 599
|
| 1297 |
+
},
|
| 1298 |
+
"operator_mod": {
|
| 1299 |
+
"opcode": "operator_mod",
|
| 1300 |
+
"next": null,
|
| 1301 |
+
"parent": null,
|
| 1302 |
+
"inputs": {
|
| 1303 |
+
"NUM1": [
|
| 1304 |
+
1,
|
| 1305 |
+
[
|
| 1306 |
+
4,
|
| 1307 |
+
""
|
| 1308 |
+
]
|
| 1309 |
+
],
|
| 1310 |
+
"NUM2": [
|
| 1311 |
+
1,
|
| 1312 |
+
[
|
| 1313 |
+
4,
|
| 1314 |
+
""
|
| 1315 |
+
]
|
| 1316 |
+
]
|
| 1317 |
+
},
|
| 1318 |
+
"fields": {},
|
| 1319 |
+
"shadow": false,
|
| 1320 |
+
"topLevel": true,
|
| 1321 |
+
"x": 295,
|
| 1322 |
+
"y": 594
|
| 1323 |
+
},
|
| 1324 |
+
"operator_round": {
|
| 1325 |
+
"opcode": "operator_round",
|
| 1326 |
+
"next": null,
|
| 1327 |
+
"parent": null,
|
| 1328 |
+
"inputs": {
|
| 1329 |
+
"NUM": [
|
| 1330 |
+
1,
|
| 1331 |
+
[
|
| 1332 |
+
4,
|
| 1333 |
+
""
|
| 1334 |
+
]
|
| 1335 |
+
]
|
| 1336 |
+
},
|
| 1337 |
+
"fields": {},
|
| 1338 |
+
"shadow": false,
|
| 1339 |
+
"topLevel": true,
|
| 1340 |
+
"x": 307,
|
| 1341 |
+
"y": 674
|
| 1342 |
+
},
|
| 1343 |
+
"operator_mathop": {
|
| 1344 |
+
"opcode": "operator_mathop",
|
| 1345 |
+
"next": null,
|
| 1346 |
+
"parent": null,
|
| 1347 |
+
"inputs": {
|
| 1348 |
+
"NUM": [
|
| 1349 |
+
1,
|
| 1350 |
+
[
|
| 1351 |
+
4,
|
| 1352 |
+
""
|
| 1353 |
+
]
|
| 1354 |
+
]
|
| 1355 |
+
},
|
| 1356 |
+
"fields": {
|
| 1357 |
+
"OPERATOR": [
|
| 1358 |
+
"abs",
|
| 1359 |
+
null
|
| 1360 |
+
]
|
| 1361 |
+
},
|
| 1362 |
+
"shadow": false,
|
| 1363 |
+
"topLevel": true,
|
| 1364 |
+
"x": 280,
|
| 1365 |
+
"y": 754
|
| 1366 |
+
},
|
| 1367 |
+
"data_setvariableto": {
|
| 1368 |
+
"opcode": "data_setvariableto",
|
| 1369 |
+
"next": null,
|
| 1370 |
+
"parent": null,
|
| 1371 |
+
"inputs": {
|
| 1372 |
+
"VALUE": [
|
| 1373 |
+
1,
|
| 1374 |
+
[
|
| 1375 |
+
10,
|
| 1376 |
+
"0"
|
| 1377 |
+
]
|
| 1378 |
+
]
|
| 1379 |
+
},
|
| 1380 |
+
"fields": {
|
| 1381 |
+
"VARIABLE": [
|
| 1382 |
+
"my variable",
|
| 1383 |
+
"`jEk@4|i[#Fk?(8x)AV.-my variable"
|
| 1384 |
+
]
|
| 1385 |
+
},
|
| 1386 |
+
"shadow": false,
|
| 1387 |
+
"topLevel": true,
|
| 1388 |
+
"x": 348,
|
| 1389 |
+
"y": 241
|
| 1390 |
+
},
|
| 1391 |
+
"data_changevariableby": {
|
| 1392 |
+
"opcode": "data_changevariableby",
|
| 1393 |
+
"next": null,
|
| 1394 |
+
"parent": null,
|
| 1395 |
+
"inputs": {
|
| 1396 |
+
"VALUE": [
|
| 1397 |
+
1,
|
| 1398 |
+
[
|
| 1399 |
+
4,
|
| 1400 |
+
"1"
|
| 1401 |
+
]
|
| 1402 |
+
]
|
| 1403 |
+
},
|
| 1404 |
+
"fields": {
|
| 1405 |
+
"VARIABLE": [
|
| 1406 |
+
"my variable",
|
| 1407 |
+
"`jEk@4|i[#Fk?(8x)AV.-my variable"
|
| 1408 |
+
]
|
| 1409 |
+
},
|
| 1410 |
+
"shadow": false,
|
| 1411 |
+
"topLevel": true,
|
| 1412 |
+
"x": 313,
|
| 1413 |
+
"y": 363
|
| 1414 |
+
},
|
| 1415 |
+
"data_showvariable": {
|
| 1416 |
+
"opcode": "data_showvariable",
|
| 1417 |
+
"next": null,
|
| 1418 |
+
"parent": null,
|
| 1419 |
+
"inputs": {},
|
| 1420 |
+
"fields": {
|
| 1421 |
+
"VARIABLE": [
|
| 1422 |
+
"my variable",
|
| 1423 |
+
"`jEk@4|i[#Fk?(8x)AV.-my variable"
|
| 1424 |
+
]
|
| 1425 |
+
},
|
| 1426 |
+
"shadow": false,
|
| 1427 |
+
"topLevel": true,
|
| 1428 |
+
"x": 415,
|
| 1429 |
+
"y": 473
|
| 1430 |
+
},
|
| 1431 |
+
"data_hidevariable": {
|
| 1432 |
+
"opcode": "data_hidevariable",
|
| 1433 |
+
"next": null,
|
| 1434 |
+
"parent": null,
|
| 1435 |
+
"inputs": {},
|
| 1436 |
+
"fields": {
|
| 1437 |
+
"VARIABLE": [
|
| 1438 |
+
"my variable",
|
| 1439 |
+
"`jEk@4|i[#Fk?(8x)AV.-my variable"
|
| 1440 |
+
]
|
| 1441 |
+
},
|
| 1442 |
+
"shadow": false,
|
| 1443 |
+
"topLevel": true,
|
| 1444 |
+
"x": 319,
|
| 1445 |
+
"y": 587
|
| 1446 |
+
},
|
| 1447 |
+
"data_addtolist": {
|
| 1448 |
+
"opcode": "data_addtolist",
|
| 1449 |
+
"next": null,
|
| 1450 |
+
"parent": null,
|
| 1451 |
+
"inputs": {
|
| 1452 |
+
"ITEM": [
|
| 1453 |
+
1,
|
| 1454 |
+
[
|
| 1455 |
+
10,
|
| 1456 |
+
"thing"
|
| 1457 |
+
]
|
| 1458 |
+
]
|
| 1459 |
+
},
|
| 1460 |
+
"fields": {
|
| 1461 |
+
"LIST": [
|
| 1462 |
+
"MY_LIST",
|
| 1463 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 1464 |
+
]
|
| 1465 |
+
},
|
| 1466 |
+
"shadow": false,
|
| 1467 |
+
"topLevel": true,
|
| 1468 |
+
"x": 385,
|
| 1469 |
+
"y": 109
|
| 1470 |
+
},
|
| 1471 |
+
"data_deleteoflist": {
|
| 1472 |
+
"opcode": "data_deleteoflist",
|
| 1473 |
+
"next": null,
|
| 1474 |
+
"parent": null,
|
| 1475 |
+
"inputs": {
|
| 1476 |
+
"INDEX": [
|
| 1477 |
+
1,
|
| 1478 |
+
[
|
| 1479 |
+
7,
|
| 1480 |
+
"1"
|
| 1481 |
+
]
|
| 1482 |
+
]
|
| 1483 |
+
},
|
| 1484 |
+
"fields": {
|
| 1485 |
+
"LIST": [
|
| 1486 |
+
"MY_LIST",
|
| 1487 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 1488 |
+
]
|
| 1489 |
+
},
|
| 1490 |
+
"shadow": false,
|
| 1491 |
+
"topLevel": true,
|
| 1492 |
+
"x": 384,
|
| 1493 |
+
"y": 244
|
| 1494 |
+
},
|
| 1495 |
+
"data_deletealloflist": {
|
| 1496 |
+
"opcode": "data_deletealloflist",
|
| 1497 |
+
"next": null,
|
| 1498 |
+
"parent": null,
|
| 1499 |
+
"inputs": {},
|
| 1500 |
+
"fields": {
|
| 1501 |
+
"LIST": [
|
| 1502 |
+
"MY_LIST",
|
| 1503 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 1504 |
+
]
|
| 1505 |
+
},
|
| 1506 |
+
"shadow": false,
|
| 1507 |
+
"topLevel": true,
|
| 1508 |
+
"x": 387,
|
| 1509 |
+
"y": 374
|
| 1510 |
+
},
|
| 1511 |
+
"data_insertatlist": {
|
| 1512 |
+
"opcode": "data_insertatlist",
|
| 1513 |
+
"next": null,
|
| 1514 |
+
"parent": null,
|
| 1515 |
+
"inputs": {
|
| 1516 |
+
"ITEM": [
|
| 1517 |
+
1,
|
| 1518 |
+
[
|
| 1519 |
+
10,
|
| 1520 |
+
"thing"
|
| 1521 |
+
]
|
| 1522 |
+
],
|
| 1523 |
+
"INDEX": [
|
| 1524 |
+
1,
|
| 1525 |
+
[
|
| 1526 |
+
7,
|
| 1527 |
+
"1"
|
| 1528 |
+
]
|
| 1529 |
+
]
|
| 1530 |
+
},
|
| 1531 |
+
"fields": {
|
| 1532 |
+
"LIST": [
|
| 1533 |
+
"MY_LIST",
|
| 1534 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 1535 |
+
]
|
| 1536 |
+
},
|
| 1537 |
+
"shadow": false,
|
| 1538 |
+
"topLevel": true,
|
| 1539 |
+
"x": 366,
|
| 1540 |
+
"y": 527
|
| 1541 |
+
},
|
| 1542 |
+
"data_replaceitemoflist": {
|
| 1543 |
+
"opcode": "data_replaceitemoflist",
|
| 1544 |
+
"next": null,
|
| 1545 |
+
"parent": null,
|
| 1546 |
+
"inputs": {
|
| 1547 |
+
"INDEX": [
|
| 1548 |
+
1,
|
| 1549 |
+
[
|
| 1550 |
+
7,
|
| 1551 |
+
"1"
|
| 1552 |
+
]
|
| 1553 |
+
],
|
| 1554 |
+
"ITEM": [
|
| 1555 |
+
1,
|
| 1556 |
+
[
|
| 1557 |
+
10,
|
| 1558 |
+
"thing"
|
| 1559 |
+
]
|
| 1560 |
+
]
|
| 1561 |
+
},
|
| 1562 |
+
"fields": {
|
| 1563 |
+
"LIST": [
|
| 1564 |
+
"MY_LIST",
|
| 1565 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 1566 |
+
]
|
| 1567 |
+
},
|
| 1568 |
+
"shadow": false,
|
| 1569 |
+
"topLevel": true,
|
| 1570 |
+
"x": 365,
|
| 1571 |
+
"y": 657
|
| 1572 |
+
},
|
| 1573 |
+
"data_itemoflist": {
|
| 1574 |
+
"opcode": "data_itemoflist",
|
| 1575 |
+
"next": null,
|
| 1576 |
+
"parent": null,
|
| 1577 |
+
"inputs": {
|
| 1578 |
+
"INDEX": [
|
| 1579 |
+
1,
|
| 1580 |
+
[
|
| 1581 |
+
7,
|
| 1582 |
+
"1"
|
| 1583 |
+
]
|
| 1584 |
+
]
|
| 1585 |
+
},
|
| 1586 |
+
"fields": {
|
| 1587 |
+
"LIST": [
|
| 1588 |
+
"MY_LIST",
|
| 1589 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 1590 |
+
]
|
| 1591 |
+
},
|
| 1592 |
+
"shadow": false,
|
| 1593 |
+
"topLevel": true,
|
| 1594 |
+
"x": 862,
|
| 1595 |
+
"y": 117
|
| 1596 |
+
},
|
| 1597 |
+
"data_itemnumoflist": {
|
| 1598 |
+
"opcode": "data_itemnumoflist",
|
| 1599 |
+
"next": null,
|
| 1600 |
+
"parent": null,
|
| 1601 |
+
"inputs": {
|
| 1602 |
+
"ITEM": [
|
| 1603 |
+
1,
|
| 1604 |
+
[
|
| 1605 |
+
10,
|
| 1606 |
+
"thing"
|
| 1607 |
+
]
|
| 1608 |
+
]
|
| 1609 |
+
},
|
| 1610 |
+
"fields": {
|
| 1611 |
+
"LIST": [
|
| 1612 |
+
"MY_LIST",
|
| 1613 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 1614 |
+
]
|
| 1615 |
+
},
|
| 1616 |
+
"shadow": false,
|
| 1617 |
+
"topLevel": true,
|
| 1618 |
+
"x": 883,
|
| 1619 |
+
"y": 238
|
| 1620 |
+
},
|
| 1621 |
+
"data_lengthoflist": {
|
| 1622 |
+
"opcode": "data_lengthoflist",
|
| 1623 |
+
"next": null,
|
| 1624 |
+
"parent": null,
|
| 1625 |
+
"inputs": {},
|
| 1626 |
+
"fields": {
|
| 1627 |
+
"LIST": [
|
| 1628 |
+
"MY_LIST",
|
| 1629 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 1630 |
+
]
|
| 1631 |
+
},
|
| 1632 |
+
"shadow": false,
|
| 1633 |
+
"topLevel": true,
|
| 1634 |
+
"x": 876,
|
| 1635 |
+
"y": 342
|
| 1636 |
+
},
|
| 1637 |
+
"data_listcontainsitem": {
|
| 1638 |
+
"opcode": "data_listcontainsitem",
|
| 1639 |
+
"next": null,
|
| 1640 |
+
"parent": null,
|
| 1641 |
+
"inputs": {
|
| 1642 |
+
"ITEM": [
|
| 1643 |
+
1,
|
| 1644 |
+
[
|
| 1645 |
+
10,
|
| 1646 |
+
"thing"
|
| 1647 |
+
]
|
| 1648 |
+
]
|
| 1649 |
+
},
|
| 1650 |
+
"fields": {
|
| 1651 |
+
"LIST": [
|
| 1652 |
+
"MY_LIST",
|
| 1653 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 1654 |
+
]
|
| 1655 |
+
},
|
| 1656 |
+
"shadow": false,
|
| 1657 |
+
"topLevel": true,
|
| 1658 |
+
"x": 871,
|
| 1659 |
+
"y": 463
|
| 1660 |
+
},
|
| 1661 |
+
"data_showlist": {
|
| 1662 |
+
"opcode": "data_showlist",
|
| 1663 |
+
"next": null,
|
| 1664 |
+
"parent": null,
|
| 1665 |
+
"inputs": {},
|
| 1666 |
+
"fields": {
|
| 1667 |
+
"LIST": [
|
| 1668 |
+
"MY_LIST",
|
| 1669 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 1670 |
+
]
|
| 1671 |
+
},
|
| 1672 |
+
"shadow": false,
|
| 1673 |
+
"topLevel": true,
|
| 1674 |
+
"x": 931,
|
| 1675 |
+
"y": 563
|
| 1676 |
+
},
|
| 1677 |
+
"data_hidelist": {
|
| 1678 |
+
"opcode": "data_hidelist",
|
| 1679 |
+
"next": null,
|
| 1680 |
+
"parent": null,
|
| 1681 |
+
"inputs": {},
|
| 1682 |
+
"fields": {
|
| 1683 |
+
"LIST": [
|
| 1684 |
+
"MY_LIST",
|
| 1685 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 1686 |
+
]
|
| 1687 |
+
},
|
| 1688 |
+
"shadow": false,
|
| 1689 |
+
"topLevel": true,
|
| 1690 |
+
"x": 962,
|
| 1691 |
+
"y": 716
|
| 1692 |
+
},
|
| 1693 |
+
"sound_playuntildone": {
|
| 1694 |
+
"opcode": "sound_playuntildone",
|
| 1695 |
+
"next": null,
|
| 1696 |
+
"parent": null,
|
| 1697 |
+
"inputs": {
|
| 1698 |
+
"SOUND_MENU": [
|
| 1699 |
+
1,
|
| 1700 |
+
"4w%pR8G.yD%g-BwCj=uK"
|
| 1701 |
+
]
|
| 1702 |
+
},
|
| 1703 |
+
"fields": {},
|
| 1704 |
+
"shadow": false,
|
| 1705 |
+
"topLevel": true,
|
| 1706 |
+
"x": 253,
|
| 1707 |
+
"y": 17
|
| 1708 |
+
},
|
| 1709 |
+
"sound_sounds_menu": {
|
| 1710 |
+
"opcode": "sound_sounds_menu",
|
| 1711 |
+
"next": null,
|
| 1712 |
+
"parent": "Pdc$U;s8e_uUfTX`}jOo",
|
| 1713 |
+
"inputs": {},
|
| 1714 |
+
"fields": {
|
| 1715 |
+
"SOUND_MENU": [
|
| 1716 |
+
"Meow",
|
| 1717 |
+
null
|
| 1718 |
+
]
|
| 1719 |
+
},
|
| 1720 |
+
"shadow": true,
|
| 1721 |
+
"topLevel": false
|
| 1722 |
+
},
|
| 1723 |
+
"sound_play": {
|
| 1724 |
+
"opcode": "sound_play",
|
| 1725 |
+
"next": null,
|
| 1726 |
+
"parent": null,
|
| 1727 |
+
"inputs": {
|
| 1728 |
+
"SOUND_MENU": [
|
| 1729 |
+
1,
|
| 1730 |
+
"i1U{^VHb*2`9?l}=:L)/"
|
| 1731 |
+
]
|
| 1732 |
+
},
|
| 1733 |
+
"fields": {},
|
| 1734 |
+
"shadow": false,
|
| 1735 |
+
"topLevel": true,
|
| 1736 |
+
"x": 245,
|
| 1737 |
+
"y": 122
|
| 1738 |
+
},
|
| 1739 |
+
"sound_stopallsounds": {
|
| 1740 |
+
"opcode": "sound_stopallsounds",
|
| 1741 |
+
"next": null,
|
| 1742 |
+
"parent": null,
|
| 1743 |
+
"inputs": {},
|
| 1744 |
+
"fields": {},
|
| 1745 |
+
"shadow": false,
|
| 1746 |
+
"topLevel": true,
|
| 1747 |
+
"x": 253,
|
| 1748 |
+
"y": 245
|
| 1749 |
+
},
|
| 1750 |
+
"sound_changeeffectby": {
|
| 1751 |
+
"opcode": "sound_changeeffectby",
|
| 1752 |
+
"next": null,
|
| 1753 |
+
"parent": null,
|
| 1754 |
+
"inputs": {
|
| 1755 |
+
"VALUE": [
|
| 1756 |
+
1,
|
| 1757 |
+
[
|
| 1758 |
+
4,
|
| 1759 |
+
"10"
|
| 1760 |
+
]
|
| 1761 |
+
]
|
| 1762 |
+
},
|
| 1763 |
+
"fields": {
|
| 1764 |
+
"EFFECT": [
|
| 1765 |
+
"PITCH",
|
| 1766 |
+
null
|
| 1767 |
+
]
|
| 1768 |
+
},
|
| 1769 |
+
"shadow": false,
|
| 1770 |
+
"topLevel": true,
|
| 1771 |
+
"x": 653,
|
| 1772 |
+
"y": 14
|
| 1773 |
+
},
|
| 1774 |
+
"sound_seteffectto": {
|
| 1775 |
+
"opcode": "sound_seteffectto",
|
| 1776 |
+
"next": null,
|
| 1777 |
+
"parent": null,
|
| 1778 |
+
"inputs": {
|
| 1779 |
+
"VALUE": [
|
| 1780 |
+
1,
|
| 1781 |
+
[
|
| 1782 |
+
4,
|
| 1783 |
+
"100"
|
| 1784 |
+
]
|
| 1785 |
+
]
|
| 1786 |
+
},
|
| 1787 |
+
"fields": {
|
| 1788 |
+
"EFFECT": [
|
| 1789 |
+
"PITCH",
|
| 1790 |
+
null
|
| 1791 |
+
]
|
| 1792 |
+
},
|
| 1793 |
+
"shadow": false,
|
| 1794 |
+
"topLevel": true,
|
| 1795 |
+
"x": 653,
|
| 1796 |
+
"y": 139
|
| 1797 |
+
},
|
| 1798 |
+
"sound_cleareffects": {
|
| 1799 |
+
"opcode": "sound_cleareffects",
|
| 1800 |
+
"next": null,
|
| 1801 |
+
"parent": null,
|
| 1802 |
+
"inputs": {},
|
| 1803 |
+
"fields": {},
|
| 1804 |
+
"shadow": false,
|
| 1805 |
+
"topLevel": true,
|
| 1806 |
+
"x": 651,
|
| 1807 |
+
"y": 242
|
| 1808 |
+
},
|
| 1809 |
+
"sound_changevolumeby": {
|
| 1810 |
+
"opcode": "sound_changevolumeby",
|
| 1811 |
+
"next": null,
|
| 1812 |
+
"parent": null,
|
| 1813 |
+
"inputs": {
|
| 1814 |
+
"VOLUME": [
|
| 1815 |
+
1,
|
| 1816 |
+
[
|
| 1817 |
+
4,
|
| 1818 |
+
"-10"
|
| 1819 |
+
]
|
| 1820 |
+
]
|
| 1821 |
+
},
|
| 1822 |
+
"fields": {},
|
| 1823 |
+
"shadow": false,
|
| 1824 |
+
"topLevel": true,
|
| 1825 |
+
"x": 645,
|
| 1826 |
+
"y": 353
|
| 1827 |
+
},
|
| 1828 |
+
"sound_setvolumeto": {
|
| 1829 |
+
"opcode": "sound_setvolumeto",
|
| 1830 |
+
"next": null,
|
| 1831 |
+
"parent": null,
|
| 1832 |
+
"inputs": {
|
| 1833 |
+
"VOLUME": [
|
| 1834 |
+
1,
|
| 1835 |
+
[
|
| 1836 |
+
4,
|
| 1837 |
+
"100"
|
| 1838 |
+
]
|
| 1839 |
+
]
|
| 1840 |
+
},
|
| 1841 |
+
"fields": {},
|
| 1842 |
+
"shadow": false,
|
| 1843 |
+
"topLevel": true,
|
| 1844 |
+
"x": 1108,
|
| 1845 |
+
"y": 5
|
| 1846 |
+
},
|
| 1847 |
+
"sound_volume": {
|
| 1848 |
+
"opcode": "sound_volume",
|
| 1849 |
+
"next": null,
|
| 1850 |
+
"parent": null,
|
| 1851 |
+
"inputs": {},
|
| 1852 |
+
"fields": {},
|
| 1853 |
+
"shadow": false,
|
| 1854 |
+
"topLevel": true,
|
| 1855 |
+
"x": 1136,
|
| 1856 |
+
"y": 123
|
| 1857 |
+
},
|
| 1858 |
+
"looks_sayforsecs": {
|
| 1859 |
+
"opcode": "looks_sayforsecs",
|
| 1860 |
+
"next": null,
|
| 1861 |
+
"parent": null,
|
| 1862 |
+
"inputs": {
|
| 1863 |
+
"MESSAGE": [
|
| 1864 |
+
1,
|
| 1865 |
+
[
|
| 1866 |
+
10,
|
| 1867 |
+
"Hello!"
|
| 1868 |
+
]
|
| 1869 |
+
],
|
| 1870 |
+
"SECS": [
|
| 1871 |
+
1,
|
| 1872 |
+
[
|
| 1873 |
+
4,
|
| 1874 |
+
"2"
|
| 1875 |
+
]
|
| 1876 |
+
]
|
| 1877 |
+
},
|
| 1878 |
+
"fields": {},
|
| 1879 |
+
"shadow": false,
|
| 1880 |
+
"topLevel": true,
|
| 1881 |
+
"x": 408,
|
| 1882 |
+
"y": 91
|
| 1883 |
+
},
|
| 1884 |
+
"looks_say": {
|
| 1885 |
+
"opcode": "looks_say",
|
| 1886 |
+
"next": null,
|
| 1887 |
+
"parent": null,
|
| 1888 |
+
"inputs": {
|
| 1889 |
+
"MESSAGE": [
|
| 1890 |
+
1,
|
| 1891 |
+
[
|
| 1892 |
+
10,
|
| 1893 |
+
"Hello!"
|
| 1894 |
+
]
|
| 1895 |
+
]
|
| 1896 |
+
},
|
| 1897 |
+
"fields": {},
|
| 1898 |
+
"shadow": false,
|
| 1899 |
+
"topLevel": true,
|
| 1900 |
+
"x": 413,
|
| 1901 |
+
"y": 213
|
| 1902 |
+
},
|
| 1903 |
+
"looks_thinkforsecs": {
|
| 1904 |
+
"opcode": "looks_thinkforsecs",
|
| 1905 |
+
"next": null,
|
| 1906 |
+
"parent": null,
|
| 1907 |
+
"inputs": {
|
| 1908 |
+
"MESSAGE": [
|
| 1909 |
+
1,
|
| 1910 |
+
[
|
| 1911 |
+
10,
|
| 1912 |
+
"Hmm..."
|
| 1913 |
+
]
|
| 1914 |
+
],
|
| 1915 |
+
"SECS": [
|
| 1916 |
+
1,
|
| 1917 |
+
[
|
| 1918 |
+
4,
|
| 1919 |
+
"2"
|
| 1920 |
+
]
|
| 1921 |
+
]
|
| 1922 |
+
},
|
| 1923 |
+
"fields": {},
|
| 1924 |
+
"shadow": false,
|
| 1925 |
+
"topLevel": true,
|
| 1926 |
+
"x": 413,
|
| 1927 |
+
"y": 317
|
| 1928 |
+
},
|
| 1929 |
+
"looks_think": {
|
| 1930 |
+
"opcode": "looks_think",
|
| 1931 |
+
"next": null,
|
| 1932 |
+
"parent": null,
|
| 1933 |
+
"inputs": {
|
| 1934 |
+
"MESSAGE": [
|
| 1935 |
+
1,
|
| 1936 |
+
[
|
| 1937 |
+
10,
|
| 1938 |
+
"Hmm..."
|
| 1939 |
+
]
|
| 1940 |
+
]
|
| 1941 |
+
},
|
| 1942 |
+
"fields": {},
|
| 1943 |
+
"shadow": false,
|
| 1944 |
+
"topLevel": true,
|
| 1945 |
+
"x": 412,
|
| 1946 |
+
"y": 432
|
| 1947 |
+
},
|
| 1948 |
+
"looks_switchcostumeto": {
|
| 1949 |
+
"opcode": "looks_switchcostumeto",
|
| 1950 |
+
"next": null,
|
| 1951 |
+
"parent": null,
|
| 1952 |
+
"inputs": {
|
| 1953 |
+
"COSTUME": [
|
| 1954 |
+
1,
|
| 1955 |
+
"8;bti4wv(iH9nkOacCJ|"
|
| 1956 |
+
]
|
| 1957 |
+
},
|
| 1958 |
+
"fields": {},
|
| 1959 |
+
"shadow": false,
|
| 1960 |
+
"topLevel": true,
|
| 1961 |
+
"x": 411,
|
| 1962 |
+
"y": 555
|
| 1963 |
+
},
|
| 1964 |
+
"looks_costume": {
|
| 1965 |
+
"opcode": "looks_costume",
|
| 1966 |
+
"next": null,
|
| 1967 |
+
"parent": "Q#a,6LPWHqo9-0Nu*[SV",
|
| 1968 |
+
"inputs": {},
|
| 1969 |
+
"fields": {
|
| 1970 |
+
"COSTUME": [
|
| 1971 |
+
"costume2",
|
| 1972 |
+
null
|
| 1973 |
+
]
|
| 1974 |
+
},
|
| 1975 |
+
"shadow": true,
|
| 1976 |
+
"topLevel": false
|
| 1977 |
+
},
|
| 1978 |
+
"looks_nextcostume": {
|
| 1979 |
+
"opcode": "looks_nextcostume",
|
| 1980 |
+
"next": null,
|
| 1981 |
+
"parent": null,
|
| 1982 |
+
"inputs": {},
|
| 1983 |
+
"fields": {},
|
| 1984 |
+
"shadow": false,
|
| 1985 |
+
"topLevel": true,
|
| 1986 |
+
"x": 419,
|
| 1987 |
+
"y": 687
|
| 1988 |
+
},
|
| 1989 |
+
"looks_switchbackdropto": {
|
| 1990 |
+
"opcode": "looks_switchbackdropto",
|
| 1991 |
+
"next": null,
|
| 1992 |
+
"parent": null,
|
| 1993 |
+
"inputs": {
|
| 1994 |
+
"BACKDROP": [
|
| 1995 |
+
1,
|
| 1996 |
+
"-?yeX}29V*wd6W:unW0i"
|
| 1997 |
+
]
|
| 1998 |
+
},
|
| 1999 |
+
"fields": {},
|
| 2000 |
+
"shadow": false,
|
| 2001 |
+
"topLevel": true,
|
| 2002 |
+
"x": 901,
|
| 2003 |
+
"y": 91
|
| 2004 |
+
},
|
| 2005 |
+
"looks_backdrops": {
|
| 2006 |
+
"opcode": "looks_backdrops",
|
| 2007 |
+
"next": null,
|
| 2008 |
+
"parent": "`Wm^p~l[(IWzc1|wNv*.",
|
| 2009 |
+
"inputs": {},
|
| 2010 |
+
"fields": {
|
| 2011 |
+
"BACKDROP": [
|
| 2012 |
+
"backdrop1",
|
| 2013 |
+
null
|
| 2014 |
+
]
|
| 2015 |
+
},
|
| 2016 |
+
"shadow": true,
|
| 2017 |
+
"topLevel": false
|
| 2018 |
+
},
|
| 2019 |
+
"looks_changesizeby": {
|
| 2020 |
+
"opcode": "looks_changesizeby",
|
| 2021 |
+
"next": null,
|
| 2022 |
+
"parent": null,
|
| 2023 |
+
"inputs": {
|
| 2024 |
+
"CHANGE": [
|
| 2025 |
+
1,
|
| 2026 |
+
[
|
| 2027 |
+
4,
|
| 2028 |
+
"10"
|
| 2029 |
+
]
|
| 2030 |
+
]
|
| 2031 |
+
},
|
| 2032 |
+
"fields": {},
|
| 2033 |
+
"shadow": false,
|
| 2034 |
+
"topLevel": true,
|
| 2035 |
+
"x": 895,
|
| 2036 |
+
"y": 192
|
| 2037 |
+
},
|
| 2038 |
+
"looks_setsizeto": {
|
| 2039 |
+
"opcode": "looks_setsizeto",
|
| 2040 |
+
"next": null,
|
| 2041 |
+
"parent": null,
|
| 2042 |
+
"inputs": {
|
| 2043 |
+
"SIZE": [
|
| 2044 |
+
1,
|
| 2045 |
+
[
|
| 2046 |
+
4,
|
| 2047 |
+
"100"
|
| 2048 |
+
]
|
| 2049 |
+
]
|
| 2050 |
+
},
|
| 2051 |
+
"fields": {},
|
| 2052 |
+
"shadow": false,
|
| 2053 |
+
"topLevel": true,
|
| 2054 |
+
"x": 896,
|
| 2055 |
+
"y": 303
|
| 2056 |
+
},
|
| 2057 |
+
"looks_changeeffectby": {
|
| 2058 |
+
"opcode": "looks_changeeffectby",
|
| 2059 |
+
"next": null,
|
| 2060 |
+
"parent": null,
|
| 2061 |
+
"inputs": {
|
| 2062 |
+
"CHANGE": [
|
| 2063 |
+
1,
|
| 2064 |
+
[
|
| 2065 |
+
4,
|
| 2066 |
+
"25"
|
| 2067 |
+
]
|
| 2068 |
+
]
|
| 2069 |
+
},
|
| 2070 |
+
"fields": {
|
| 2071 |
+
"EFFECT": [
|
| 2072 |
+
"COLOR",
|
| 2073 |
+
null
|
| 2074 |
+
]
|
| 2075 |
+
},
|
| 2076 |
+
"shadow": false,
|
| 2077 |
+
"topLevel": true,
|
| 2078 |
+
"x": 892,
|
| 2079 |
+
"y": 416
|
| 2080 |
+
},
|
| 2081 |
+
"looks_seteffectto": {
|
| 2082 |
+
"opcode": "looks_seteffectto",
|
| 2083 |
+
"next": null,
|
| 2084 |
+
"parent": null,
|
| 2085 |
+
"inputs": {
|
| 2086 |
+
"VALUE": [
|
| 2087 |
+
1,
|
| 2088 |
+
[
|
| 2089 |
+
4,
|
| 2090 |
+
"0"
|
| 2091 |
+
]
|
| 2092 |
+
]
|
| 2093 |
+
},
|
| 2094 |
+
"fields": {
|
| 2095 |
+
"EFFECT": [
|
| 2096 |
+
"COLOR",
|
| 2097 |
+
null
|
| 2098 |
+
]
|
| 2099 |
+
},
|
| 2100 |
+
"shadow": false,
|
| 2101 |
+
"topLevel": true,
|
| 2102 |
+
"x": 902,
|
| 2103 |
+
"y": 527
|
| 2104 |
+
},
|
| 2105 |
+
"looks_cleargraphiceffects": {
|
| 2106 |
+
"opcode": "looks_cleargraphiceffects",
|
| 2107 |
+
"next": null,
|
| 2108 |
+
"parent": null,
|
| 2109 |
+
"inputs": {},
|
| 2110 |
+
"fields": {},
|
| 2111 |
+
"shadow": false,
|
| 2112 |
+
"topLevel": true,
|
| 2113 |
+
"x": 902,
|
| 2114 |
+
"y": 638
|
| 2115 |
+
},
|
| 2116 |
+
"looks_show": {
|
| 2117 |
+
"opcode": "looks_show",
|
| 2118 |
+
"next": null,
|
| 2119 |
+
"parent": null,
|
| 2120 |
+
"inputs": {},
|
| 2121 |
+
"fields": {},
|
| 2122 |
+
"shadow": false,
|
| 2123 |
+
"topLevel": true,
|
| 2124 |
+
"x": 908,
|
| 2125 |
+
"y": 758
|
| 2126 |
+
},
|
| 2127 |
+
"looks_hide": {
|
| 2128 |
+
"opcode": "looks_hide",
|
| 2129 |
+
"next": null,
|
| 2130 |
+
"parent": null,
|
| 2131 |
+
"inputs": {},
|
| 2132 |
+
"fields": {},
|
| 2133 |
+
"shadow": false,
|
| 2134 |
+
"topLevel": true,
|
| 2135 |
+
"x": 455,
|
| 2136 |
+
"y": 861
|
| 2137 |
+
},
|
| 2138 |
+
"looks_gotofrontback": {
|
| 2139 |
+
"opcode": "looks_gotofrontback",
|
| 2140 |
+
"next": null,
|
| 2141 |
+
"parent": null,
|
| 2142 |
+
"inputs": {},
|
| 2143 |
+
"fields": {
|
| 2144 |
+
"FRONT_BACK": [
|
| 2145 |
+
"front",
|
| 2146 |
+
null
|
| 2147 |
+
]
|
| 2148 |
+
},
|
| 2149 |
+
"shadow": false,
|
| 2150 |
+
"topLevel": true,
|
| 2151 |
+
"x": 853,
|
| 2152 |
+
"y": 878
|
| 2153 |
+
},
|
| 2154 |
+
"looks_goforwardbackwardlayers": {
|
| 2155 |
+
"opcode": "looks_goforwardbackwardlayers",
|
| 2156 |
+
"next": null,
|
| 2157 |
+
"parent": null,
|
| 2158 |
+
"inputs": {
|
| 2159 |
+
"NUM": [
|
| 2160 |
+
1,
|
| 2161 |
+
[
|
| 2162 |
+
7,
|
| 2163 |
+
"1"
|
| 2164 |
+
]
|
| 2165 |
+
]
|
| 2166 |
+
},
|
| 2167 |
+
"fields": {
|
| 2168 |
+
"FORWARD_BACKWARD": [
|
| 2169 |
+
"forward",
|
| 2170 |
+
null
|
| 2171 |
+
]
|
| 2172 |
+
},
|
| 2173 |
+
"shadow": false,
|
| 2174 |
+
"topLevel": true,
|
| 2175 |
+
"x": 851,
|
| 2176 |
+
"y": 999
|
| 2177 |
+
},
|
| 2178 |
+
"looks_costumenumbername": {
|
| 2179 |
+
"opcode": "looks_costumenumbername",
|
| 2180 |
+
"next": null,
|
| 2181 |
+
"parent": null,
|
| 2182 |
+
"inputs": {},
|
| 2183 |
+
"fields": {
|
| 2184 |
+
"NUMBER_NAME": [
|
| 2185 |
+
"number",
|
| 2186 |
+
null
|
| 2187 |
+
]
|
| 2188 |
+
},
|
| 2189 |
+
"shadow": false,
|
| 2190 |
+
"topLevel": true,
|
| 2191 |
+
"x": 458,
|
| 2192 |
+
"y": 1007
|
| 2193 |
+
},
|
| 2194 |
+
"looks_backdropnumbername": {
|
| 2195 |
+
"opcode": "looks_backdropnumbername",
|
| 2196 |
+
"next": null,
|
| 2197 |
+
"parent": null,
|
| 2198 |
+
"inputs": {},
|
| 2199 |
+
"fields": {
|
| 2200 |
+
"NUMBER_NAME": [
|
| 2201 |
+
"number",
|
| 2202 |
+
null
|
| 2203 |
+
]
|
| 2204 |
+
},
|
| 2205 |
+
"shadow": false,
|
| 2206 |
+
"topLevel": true,
|
| 2207 |
+
"x": 1242,
|
| 2208 |
+
"y": 753
|
| 2209 |
+
},
|
| 2210 |
+
"looks_size": {
|
| 2211 |
+
"opcode": "looks_size",
|
| 2212 |
+
"next": null,
|
| 2213 |
+
"parent": null,
|
| 2214 |
+
"inputs": {},
|
| 2215 |
+
"fields": {},
|
| 2216 |
+
"shadow": false,
|
| 2217 |
+
"topLevel": true,
|
| 2218 |
+
"x": 1249,
|
| 2219 |
+
"y": 876
|
| 2220 |
+
}
|
| 2221 |
+
}
|
blocks/boolean_blocks.json
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"block_category": "Boolean Blocks",
|
| 3 |
+
"description": "Boolean blocks are hexagonal in shape. They represent conditions that evaluate to either 'true' or 'false' and are typically used as inputs for control flow blocks.",
|
| 4 |
+
"blocks": [
|
| 5 |
+
{
|
| 6 |
+
"block_name": "<() < ()>",
|
| 7 |
+
"block_type": "operator",
|
| 8 |
+
"op_code": "operator_lt",
|
| 9 |
+
"block_shape": "Boolean Block",
|
| 10 |
+
"functionality": "Checks if the first value is less than the second.",
|
| 11 |
+
"inputs": [
|
| 12 |
+
{"name": "OPERAND1", "type": "any"},
|
| 13 |
+
{"name": "OPERAND2", "type": "any"}
|
| 14 |
+
],
|
| 15 |
+
"example_standalone": "<(score) < (10)>",
|
| 16 |
+
"example_with_other_blocks": [
|
| 17 |
+
{
|
| 18 |
+
"script": "if <(score) < (10)> then\n say [Keep trying!] \nend",
|
| 19 |
+
"explanation": "This script causes the sprite to say 'Keep trying!' if the 'score' variable is less than 10."
|
| 20 |
+
}
|
| 21 |
+
]
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"block_name": "<() = ()>",
|
| 25 |
+
"block_type": "operator",
|
| 26 |
+
"op_code": "operator_equals",
|
| 27 |
+
"block_shape": "Boolean Block",
|
| 28 |
+
"functionality": "Checks if two values are equal.",
|
| 29 |
+
"inputs": [
|
| 30 |
+
{"name": "OPERAND1", "type": "any"},
|
| 31 |
+
{"name": "OPERAND2", "type": "any"}
|
| 32 |
+
],
|
| 33 |
+
"example_standalone": "<(answer) = (5)>",
|
| 34 |
+
"example_with_other_blocks": [
|
| 35 |
+
{
|
| 36 |
+
"script": "if <(answer) = (5)> then\n say [Correct!] \nend",
|
| 37 |
+
"explanation": "This script makes the sprite say 'Correct!' if the value of the 'answer' variable is exactly 5."
|
| 38 |
+
}
|
| 39 |
+
]
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"block_name": "<() > ()>",
|
| 43 |
+
"block_type": "operator",
|
| 44 |
+
"op_code": "operator_gt",
|
| 45 |
+
"block_shape": "Boolean Block",
|
| 46 |
+
"functionality": "Checks if the first value is greater than the second.",
|
| 47 |
+
"inputs": [
|
| 48 |
+
{"name": "OPERAND1", "type": "any"},
|
| 49 |
+
{"name": "OPERAND2", "type": "any"}
|
| 50 |
+
],
|
| 51 |
+
"example_standalone": "<([health v]) > (0)>",
|
| 52 |
+
"example_with_other_blocks": [
|
| 53 |
+
{
|
| 54 |
+
"script": "if <([health v]) > (0)> then\n move (10) steps\nelse\n stop [all v]\nend",
|
| 55 |
+
"explanation": "This script moves the sprite if its 'health' is greater than 0; otherwise, it stops all scripts."
|
| 56 |
+
}
|
| 57 |
+
]
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
"block_name": "<<> and <>>",
|
| 61 |
+
"block_type": "operator",
|
| 62 |
+
"op_code": "operator_and",
|
| 63 |
+
"block_shape": "Boolean Block",
|
| 64 |
+
"functionality": "Returns 'true' if both provided Boolean conditions are 'true'.",
|
| 65 |
+
"inputs": [
|
| 66 |
+
{"name": "OPERAND1", "type": "boolean"},
|
| 67 |
+
{"name": "OPERAND2", "type": "boolean"}
|
| 68 |
+
],
|
| 69 |
+
"example_standalone": "<<mouse down?> and <touching [mouse-pointer]?> >",
|
| 70 |
+
"example_with_other_blocks": [
|
| 71 |
+
{
|
| 72 |
+
"script": "if <<mouse down?> and <touching [mouse-pointer]?> > then\n say [You're clicking me!]\nend",
|
| 73 |
+
"explanation": "This script makes the sprite say 'You're clicking me!' only if the mouse button is pressed AND the mouse pointer is touching the sprite."
|
| 74 |
+
}
|
| 75 |
+
]
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"block_name": "<<> or <>>",
|
| 79 |
+
"block_type": "operator",
|
| 80 |
+
"op_code": "operator_or",
|
| 81 |
+
"block_shape": "Boolean Block",
|
| 82 |
+
"functionality": "Returns 'true' if at least one of the provided Boolean conditions is 'true'.",
|
| 83 |
+
"inputs": [
|
| 84 |
+
{"name": "OPERAND1", "type": "boolean"},
|
| 85 |
+
{"name": "OPERAND2", "type": "boolean"}
|
| 86 |
+
],
|
| 87 |
+
"example_standalone": "<<key [left arrow v] pressed?> or <key [a v] pressed?>>",
|
| 88 |
+
"example_with_other_blocks": [
|
| 89 |
+
{
|
| 90 |
+
"script": "if <<key [left arrow v] pressed?> or <key [a v] pressed?>> then\n change x by (-10)\nend",
|
| 91 |
+
"explanation": "This script moves the sprite left if either the left arrow key OR the 'a' key is pressed."
|
| 92 |
+
}
|
| 93 |
+
]
|
| 94 |
+
},
|
| 95 |
+
{
|
| 96 |
+
"block_name": "<not <>>",
|
| 97 |
+
"block_type": "operator",
|
| 98 |
+
"op_code": "operator_not",
|
| 99 |
+
"block_shape": "Boolean Block",
|
| 100 |
+
"functionality": "Returns 'true' if the provided Boolean condition is 'false', and 'false' if it is 'true'.",
|
| 101 |
+
"inputs": [
|
| 102 |
+
{"name": "OPERAND", "type": "boolean"}
|
| 103 |
+
],
|
| 104 |
+
"example_standalone": "<not <mouse down?>>",
|
| 105 |
+
"example_with_other_blocks": [
|
| 106 |
+
{
|
| 107 |
+
"script": "if <not <touching [Sprite2 v]?>> then\n say [I'm safe!]\nend",
|
| 108 |
+
"explanation": "This script makes the sprite say 'I'm safe!' if it is NOT touching 'Sprite2'."
|
| 109 |
+
}
|
| 110 |
+
]
|
| 111 |
+
},
|
| 112 |
+
{
|
| 113 |
+
"block_name": "<() contains ()?>",
|
| 114 |
+
"block_type": "operator",
|
| 115 |
+
"op_code": "operator_contains",
|
| 116 |
+
"block_shape": "Boolean Block",
|
| 117 |
+
"functionality": "Checks if one string contains another string.",
|
| 118 |
+
"inputs": [
|
| 119 |
+
{"name": "STRING1", "type": "string"},
|
| 120 |
+
{"name": "STRING2", "type": "string"}
|
| 121 |
+
],
|
| 122 |
+
"example_standalone": "<[apple v] contains [a v]?>",
|
| 123 |
+
"example_with_other_blocks": [
|
| 124 |
+
{
|
| 125 |
+
"script": "if <[answer] contains [yes]?> then\n say [Great!]\nend",
|
| 126 |
+
"explanation": "This script makes the sprite say 'Great!' if the 'answer' variable contains the substring 'yes'."
|
| 127 |
+
}
|
| 128 |
+
]
|
| 129 |
+
},
|
| 130 |
+
{
|
| 131 |
+
"block_name": "<touching [edge v]?>",
|
| 132 |
+
"block_type": "Sensing",
|
| 133 |
+
"op_code": "sensing_touchingobject",
|
| 134 |
+
"block_shape": "Boolean Block",
|
| 135 |
+
"functionality": "Checks if its sprite is touching the mouse-pointer, edge, or another specified sprite.",
|
| 136 |
+
"inputs": [
|
| 137 |
+
{"name": "TOUCHINGOBJECTMENU", "type": "dropdown", "options": ["mouse-pointer", "edge", "Sprite1", "..." ]}
|
| 138 |
+
],
|
| 139 |
+
"example_standalone": "<touching [edge v]?>",
|
| 140 |
+
"example_with_other_blocks": [
|
| 141 |
+
{
|
| 142 |
+
"script": "if <touching [Sprite v]?> then\n broadcast [Game Over v] \nend",
|
| 143 |
+
"explanation": "This script makes the broadcast message 'Game Over' in script if it comes into contact with the sprite."
|
| 144 |
+
},
|
| 145 |
+
{
|
| 146 |
+
"script": "if <touching [edge v]?> then\n bounce off edge\nend",
|
| 147 |
+
"explanation": "This script makes the sprite reverse direction if it comes into contact with the edge of the stage."
|
| 148 |
+
}
|
| 149 |
+
]
|
| 150 |
+
},
|
| 151 |
+
{
|
| 152 |
+
"block_name": "<touching color ()?>",
|
| 153 |
+
"block_type": "Sensing",
|
| 154 |
+
"op_code": "sensing_touchingcolor",
|
| 155 |
+
"block_shape": "Boolean Block",
|
| 156 |
+
"functionality": "Checks whether its sprite is touching a specified color.",
|
| 157 |
+
"inputs": [
|
| 158 |
+
{"name": "COLOR", "type": "color"}
|
| 159 |
+
],
|
| 160 |
+
"example_standalone": "<touching color [#FF0000]?>",
|
| 161 |
+
"example_with_other_blocks": [
|
| 162 |
+
{
|
| 163 |
+
"script": "if <touching color [#FF0000]?> then\n change [health v] by (-1)\nend",
|
| 164 |
+
"explanation": "This script decreases the 'health' variable by 1 if the sprite touches any red color on the stage."
|
| 165 |
+
}
|
| 166 |
+
]
|
| 167 |
+
},
|
| 168 |
+
{
|
| 169 |
+
"block_name": "<color () is touching ()?>",
|
| 170 |
+
"block_type": "Sensing",
|
| 171 |
+
"op_code": "sensing_coloristouchingcolor",
|
| 172 |
+
"block_shape": "Boolean Block",
|
| 173 |
+
"functionality": "Checks whether a specific color on its sprite is touching another specified color on the stage or another sprite.",
|
| 174 |
+
"inputs": [
|
| 175 |
+
{"name": "COLOR1", "type": "color"},
|
| 176 |
+
{"name": "COLOR2", "type": "color"}
|
| 177 |
+
],
|
| 178 |
+
"example_standalone": "<color [#00FF00] is touching [#FF0000]?>",
|
| 179 |
+
"example_with_other_blocks": [
|
| 180 |
+
{
|
| 181 |
+
"script": "if <color [#00FF00] is touching [#FF0000]?> then\n say [Collision!]\nend",
|
| 182 |
+
"explanation": "This script makes the sprite say 'Collision!' if a green part of the sprite touches a red color elsewhere in the project."
|
| 183 |
+
}
|
| 184 |
+
]
|
| 185 |
+
},
|
| 186 |
+
{
|
| 187 |
+
"block_name": "<key () pressed?>",
|
| 188 |
+
"block_type": "Sensing",
|
| 189 |
+
"op_code": "sensing_keypressed",
|
| 190 |
+
"block_shape": "Boolean Block",
|
| 191 |
+
"functionality": "Checks if a specified keyboard key is currently being pressed.",
|
| 192 |
+
"inputs": [
|
| 193 |
+
{"name": "KEY_OPTION", "type": "dropdown",
|
| 194 |
+
"options": [
|
| 195 |
+
"space",
|
| 196 |
+
"up arrow",
|
| 197 |
+
"down arrow",
|
| 198 |
+
"right arrow",
|
| 199 |
+
"left arrow",
|
| 200 |
+
"any",
|
| 201 |
+
"a",
|
| 202 |
+
"b",
|
| 203 |
+
"c",
|
| 204 |
+
"d",
|
| 205 |
+
"e",
|
| 206 |
+
"f",
|
| 207 |
+
"g",
|
| 208 |
+
"h",
|
| 209 |
+
"i",
|
| 210 |
+
"j",
|
| 211 |
+
"k",
|
| 212 |
+
"l",
|
| 213 |
+
"m",
|
| 214 |
+
"n",
|
| 215 |
+
"o",
|
| 216 |
+
"p",
|
| 217 |
+
"q",
|
| 218 |
+
"r",
|
| 219 |
+
"s",
|
| 220 |
+
"t",
|
| 221 |
+
"u",
|
| 222 |
+
"v",
|
| 223 |
+
"w",
|
| 224 |
+
"x",
|
| 225 |
+
"y",
|
| 226 |
+
"z",
|
| 227 |
+
"0",
|
| 228 |
+
"1",
|
| 229 |
+
"2",
|
| 230 |
+
"3",
|
| 231 |
+
"4",
|
| 232 |
+
"5",
|
| 233 |
+
"6",
|
| 234 |
+
"7",
|
| 235 |
+
"8",
|
| 236 |
+
"9"
|
| 237 |
+
]}
|
| 238 |
+
],
|
| 239 |
+
"example_standalone": "<key [space v] pressed?>",
|
| 240 |
+
"example_with_other_blocks": [
|
| 241 |
+
{
|
| 242 |
+
"script": "forever\n if <key [space v] pressed?> then\n broadcast [shoot v]\n end\nend",
|
| 243 |
+
"explanation": "This script continuously checks if the space key is pressed and, if so, sends a 'shoot' broadcast."
|
| 244 |
+
}
|
| 245 |
+
]
|
| 246 |
+
},
|
| 247 |
+
{
|
| 248 |
+
"block_name": "<mouse down?>",
|
| 249 |
+
"block_type": "Sensing",
|
| 250 |
+
"op_code": "sensing_mousedown",
|
| 251 |
+
"block_shape": "Boolean Block",
|
| 252 |
+
"functionality": "Checks if the computer mouse's primary button is being clicked while the cursor is over the stage.",
|
| 253 |
+
"inputs": null,
|
| 254 |
+
"example_standalone": "<mouse down?>",
|
| 255 |
+
"example_with_other_blocks": [
|
| 256 |
+
{
|
| 257 |
+
"script": "if <mouse down?> then\n go to mouse-pointer\nend",
|
| 258 |
+
"explanation": "This script makes the sprite follow the mouse pointer only when the mouse button is held down."
|
| 259 |
+
}
|
| 260 |
+
]
|
| 261 |
+
},
|
| 262 |
+
{
|
| 263 |
+
"block_name": "<[my list v] contains ()?>",
|
| 264 |
+
"block_type": "Data",
|
| 265 |
+
"op_code": "data_listcontainsitem",
|
| 266 |
+
"block_shape": "Boolean Block",
|
| 267 |
+
"functionality": "Checks if a list includes a specific item.",
|
| 268 |
+
"inputs": [
|
| 269 |
+
{"name": "LIST", "type": "dropdown"},
|
| 270 |
+
{"name": "ITEM", "type": "any"}
|
| 271 |
+
],
|
| 272 |
+
"example_standalone": "<[inventory v] contains [key]?>",
|
| 273 |
+
"example_with_other_blocks": [
|
| 274 |
+
{
|
| 275 |
+
"script": "if <[inventory v] contains [key]?> then\n say [You have the key!]\nend",
|
| 276 |
+
"explanation": "This script makes the sprite say 'You have the key!' if the 'inventory' list contains the item 'key'."
|
| 277 |
+
}
|
| 278 |
+
]
|
| 279 |
+
}
|
| 280 |
+
]
|
| 281 |
+
}
|
blocks/c_blocks.json
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"block_category": "C Blocks",
|
| 3 |
+
"description": "C blocks are shaped like the letter 'C'. They are used to loop or conditionally execute blocks that are placed within their opening, managing the flow of scripts.",
|
| 4 |
+
"blocks": [
|
| 5 |
+
{
|
| 6 |
+
"block_name": "repeat ()",
|
| 7 |
+
"block_type": "Control",
|
| 8 |
+
"block_shape": "C-Block",
|
| 9 |
+
"op_code": "control_repeat",
|
| 10 |
+
"functionality": "Repeats the blocks inside it a specified number of times.",
|
| 11 |
+
"inputs": [
|
| 12 |
+
{
|
| 13 |
+
"name": "times",
|
| 14 |
+
"type": "number"
|
| 15 |
+
}
|
| 16 |
+
],
|
| 17 |
+
"example_standalone": "repeat (10)",
|
| 18 |
+
"example_with_other_blocks": [
|
| 19 |
+
{
|
| 20 |
+
"script": "when [space v] key pressed\n repeat (10)\n move (10) steps\n wait (0.1) seconds\n end",
|
| 21 |
+
"explanation": "This script makes the sprite move 10 steps Ten times, with a short pause after each movement on spacebar pressed."
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"script": "when [up arrow v] key pressed\n repeat (10)\n change y by (10)\n wait (0.1) seconds\n change y by (10)\n end",
|
| 25 |
+
"explanation": "This script makes the sprite jump, with a short pause after each movement on up arrow pressed."
|
| 26 |
+
}
|
| 27 |
+
]
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"block_name": "forever",
|
| 31 |
+
"block_type": "Control",
|
| 32 |
+
"block_shape": "C-Block",
|
| 33 |
+
"op_code": "control_forever",
|
| 34 |
+
"functionality": "Continuously runs the blocks inside it.",
|
| 35 |
+
"inputs": null,
|
| 36 |
+
"example_standalone": "forever",
|
| 37 |
+
"example_with_other_blocks": [
|
| 38 |
+
{
|
| 39 |
+
"script": "when green flag clicked\n forever\n move (5) steps\n if on edge, bounce\n end",
|
| 40 |
+
"explanation": "This script makes the sprite move endlessly and bounce off the edges of the stage, creating continuous motion."
|
| 41 |
+
}
|
| 42 |
+
]
|
| 43 |
+
},
|
| 44 |
+
{
|
| 45 |
+
"block_name": "if <> then",
|
| 46 |
+
"block_type": "Control",
|
| 47 |
+
"block_shape": "C-Block",
|
| 48 |
+
"op_code": "control_if",
|
| 49 |
+
"functionality": "Executes the blocks inside it only if the specified boolean condition is true. [NOTE: it takes boolean blocks as input]",
|
| 50 |
+
"inputs": [
|
| 51 |
+
{
|
| 52 |
+
"name": "condition",
|
| 53 |
+
"type": "boolean"
|
| 54 |
+
}
|
| 55 |
+
],
|
| 56 |
+
"example_standalone": "if <touching [mouse-pointer v]?> then",
|
| 57 |
+
"example_with_other_blocks": [
|
| 58 |
+
{
|
| 59 |
+
"script": "forever\n if <touching [color (red) v]?> then\n stop [this script v]\n end",
|
| 60 |
+
"explanation": "This script continuously checks if the sprite is touching a red color, and if so, it stops the current script."
|
| 61 |
+
}
|
| 62 |
+
]
|
| 63 |
+
},
|
| 64 |
+
{
|
| 65 |
+
"block_name": "if <> then else",
|
| 66 |
+
"block_type": "Control",
|
| 67 |
+
"block_shape": "C-Block",
|
| 68 |
+
"op_code": "control_if_else",
|
| 69 |
+
"functionality": "Executes one set of blocks if the specified boolean condition is true, and a different set of blocks if the condition is false. [NOTE: it takes boolean blocks as input]",
|
| 70 |
+
"inputs": [
|
| 71 |
+
{
|
| 72 |
+
"name": "condition",
|
| 73 |
+
"type": "boolean"
|
| 74 |
+
}
|
| 75 |
+
],
|
| 76 |
+
"example_standalone": "if <score > (10)> then else",
|
| 77 |
+
"example_with_other_blocks": [
|
| 78 |
+
{
|
| 79 |
+
"script": "if <(score) > (10)> then\n say [You win!] for (2) seconds\nelse\n say [Keep trying!] for (2) seconds\nend",
|
| 80 |
+
"explanation": "This script checks the 'score'. If the score is greater than 10, it says 'You win!'; otherwise, it says 'Keep trying!'."
|
| 81 |
+
}
|
| 82 |
+
]
|
| 83 |
+
},
|
| 84 |
+
{
|
| 85 |
+
"block_name": "repeat until <>",
|
| 86 |
+
"block_type": "Control",
|
| 87 |
+
"block_shape": "C-Block",
|
| 88 |
+
"op_code": "control_repeat_until",
|
| 89 |
+
"functionality": "Repeats the blocks inside it until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]",
|
| 90 |
+
"inputs": [
|
| 91 |
+
{
|
| 92 |
+
"name": "condition",
|
| 93 |
+
"type": "boolean"
|
| 94 |
+
}
|
| 95 |
+
],
|
| 96 |
+
"example_standalone": "repeat until <touching [edge v]?>",
|
| 97 |
+
"example_with_other_blocks": [
|
| 98 |
+
{
|
| 99 |
+
"script": "repeat until <touching [edge v]?>\n move (5) steps\nend",
|
| 100 |
+
"explanation": "This script makes the sprite move 5 steps repeatedly until it touches the edge of the stage."
|
| 101 |
+
}
|
| 102 |
+
]
|
| 103 |
+
}
|
| 104 |
+
]
|
| 105 |
+
}
|
blocks/cap_blocks.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"block_category": "Cap Blocks",
|
| 3 |
+
"description": "Cap blocks have a notch at the top and a flat bottom. They signify the end of a script, preventing any further blocks from being placed below them, and are used to terminate scripts or specific actions.",
|
| 4 |
+
"blocks": [
|
| 5 |
+
{
|
| 6 |
+
"block_name": "stop [v]",
|
| 7 |
+
"block_type": "Control",
|
| 8 |
+
"block_shape": "Cap Block (dynamic: can be Stack)",
|
| 9 |
+
"op_code": "control_stop",
|
| 10 |
+
"functionality": "Halts all scripts, only the current script, or other scripts within the same sprite. Its shape can dynamically change based on the selected option.",
|
| 11 |
+
"inputs": [
|
| 12 |
+
{"name": "option", "type": "dropdown", "options": ["all"]}
|
| 13 |
+
],
|
| 14 |
+
"example_standalone": "stop [all v]",
|
| 15 |
+
"example_with_other_blocks": [
|
| 16 |
+
{
|
| 17 |
+
"script": "if <(health) = (0)> then\n stop [all v]\nend",
|
| 18 |
+
"explanation": "This script stops all running scripts in the project if the 'health' variable reaches 0, typically signifying a game over condition. [9, 15]"
|
| 19 |
+
}
|
| 20 |
+
]
|
| 21 |
+
},
|
| 22 |
+
{
|
| 23 |
+
"block_name": "delete this clone",
|
| 24 |
+
"block_type": "Control",
|
| 25 |
+
"block_shape": "Cap Block",
|
| 26 |
+
"op_code": "control_delete_this_clone",
|
| 27 |
+
"functionality": "Removes the clone that is executing it from the stage.",
|
| 28 |
+
"inputs":null,
|
| 29 |
+
"example_standalone": "delete this clone",
|
| 30 |
+
"example_with_other_blocks": [
|
| 31 |
+
{
|
| 32 |
+
"script": "when I start as a clone\n wait until <touching [edge v]?>\n delete this clone\nend",
|
| 33 |
+
"explanation": "This script, run by a clone, causes the clone to disappear from the stage once it touches the edge. [1]"
|
| 34 |
+
}
|
| 35 |
+
]
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"block_name": "forever",
|
| 39 |
+
"block_type": "Control",
|
| 40 |
+
"block_shape": "Cap Block",
|
| 41 |
+
"op_code": "control_forever",
|
| 42 |
+
"functionality": "Continuously runs the blocks inside it.",
|
| 43 |
+
"inputs": null,
|
| 44 |
+
"example_standalone": "forever",
|
| 45 |
+
"example_with_other_blocks": [
|
| 46 |
+
{
|
| 47 |
+
"script": "when green flag clicked\n forever\n move (5) steps\n if on edge, bounce\n end",
|
| 48 |
+
"explanation": "This script makes the sprite move endlessly and bounce off the edges of the stage, creating continuous motion."
|
| 49 |
+
}
|
| 50 |
+
]
|
| 51 |
+
}
|
| 52 |
+
]
|
| 53 |
+
}
|
blocks/classwise_blocks/control_block.json
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"control_wait": {
|
| 3 |
+
"opcode": "control_wait",
|
| 4 |
+
"next": null,
|
| 5 |
+
"parent": null,
|
| 6 |
+
"inputs": {
|
| 7 |
+
"DURATION": [
|
| 8 |
+
1,
|
| 9 |
+
[
|
| 10 |
+
5,
|
| 11 |
+
"1"
|
| 12 |
+
]
|
| 13 |
+
]
|
| 14 |
+
},
|
| 15 |
+
"fields": {},
|
| 16 |
+
"shadow": false,
|
| 17 |
+
"topLevel": true,
|
| 18 |
+
"x": 337,
|
| 19 |
+
"y": 129
|
| 20 |
+
},
|
| 21 |
+
"control_repeat": {
|
| 22 |
+
"opcode": "control_repeat",
|
| 23 |
+
"next": null,
|
| 24 |
+
"parent": null,
|
| 25 |
+
"inputs": {
|
| 26 |
+
"TIMES": [
|
| 27 |
+
1,
|
| 28 |
+
[
|
| 29 |
+
6,
|
| 30 |
+
"10"
|
| 31 |
+
]
|
| 32 |
+
]
|
| 33 |
+
},
|
| 34 |
+
"fields": {},
|
| 35 |
+
"shadow": false,
|
| 36 |
+
"topLevel": true,
|
| 37 |
+
"x": 348,
|
| 38 |
+
"y": 265
|
| 39 |
+
},
|
| 40 |
+
"control_forever": {
|
| 41 |
+
"opcode": "control_forever",
|
| 42 |
+
"next": null,
|
| 43 |
+
"parent": null,
|
| 44 |
+
"inputs": {},
|
| 45 |
+
"fields": {},
|
| 46 |
+
"shadow": false,
|
| 47 |
+
"topLevel": true,
|
| 48 |
+
"x": 334,
|
| 49 |
+
"y": 439
|
| 50 |
+
},
|
| 51 |
+
"control_if": {
|
| 52 |
+
"opcode": "control_if",
|
| 53 |
+
"next": null,
|
| 54 |
+
"parent": null,
|
| 55 |
+
"inputs": {},
|
| 56 |
+
"fields": {},
|
| 57 |
+
"shadow": false,
|
| 58 |
+
"topLevel": true,
|
| 59 |
+
"x": 331,
|
| 60 |
+
"y": 597
|
| 61 |
+
},
|
| 62 |
+
"control_if_else": {
|
| 63 |
+
"opcode": "control_if_else",
|
| 64 |
+
"next": null,
|
| 65 |
+
"parent": null,
|
| 66 |
+
"inputs": {},
|
| 67 |
+
"fields": {},
|
| 68 |
+
"shadow": false,
|
| 69 |
+
"topLevel": true,
|
| 70 |
+
"x": 335,
|
| 71 |
+
"y": 779
|
| 72 |
+
},
|
| 73 |
+
"control_wait_until": {
|
| 74 |
+
"opcode": "control_wait_until",
|
| 75 |
+
"next": null,
|
| 76 |
+
"parent": null,
|
| 77 |
+
"inputs": {},
|
| 78 |
+
"fields": {},
|
| 79 |
+
"shadow": false,
|
| 80 |
+
"topLevel": true,
|
| 81 |
+
"x": 676,
|
| 82 |
+
"y": 285
|
| 83 |
+
},
|
| 84 |
+
"control_repeat_until": {
|
| 85 |
+
"opcode": "control_repeat_until",
|
| 86 |
+
"next": null,
|
| 87 |
+
"parent": null,
|
| 88 |
+
"inputs": {},
|
| 89 |
+
"fields": {},
|
| 90 |
+
"shadow": false,
|
| 91 |
+
"topLevel": true,
|
| 92 |
+
"x": 692,
|
| 93 |
+
"y": 381
|
| 94 |
+
},
|
| 95 |
+
"control_stop": {
|
| 96 |
+
"opcode": "control_stop",
|
| 97 |
+
"next": null,
|
| 98 |
+
"parent": null,
|
| 99 |
+
"inputs": {},
|
| 100 |
+
"fields": {
|
| 101 |
+
"STOP_OPTION": [
|
| 102 |
+
"all",
|
| 103 |
+
null
|
| 104 |
+
]
|
| 105 |
+
},
|
| 106 |
+
"shadow": false,
|
| 107 |
+
"topLevel": true,
|
| 108 |
+
"x": 708,
|
| 109 |
+
"y": 545,
|
| 110 |
+
"mutation": {
|
| 111 |
+
"tagName": "mutation",
|
| 112 |
+
"children": [],
|
| 113 |
+
"hasnext": "false"
|
| 114 |
+
}
|
| 115 |
+
},
|
| 116 |
+
"control_start_as_clone": {
|
| 117 |
+
"opcode": "control_start_as_clone",
|
| 118 |
+
"next": null,
|
| 119 |
+
"parent": null,
|
| 120 |
+
"inputs": {},
|
| 121 |
+
"fields": {},
|
| 122 |
+
"shadow": false,
|
| 123 |
+
"topLevel": true,
|
| 124 |
+
"x": 665,
|
| 125 |
+
"y": 672
|
| 126 |
+
},
|
| 127 |
+
"control_create_clone_of": {
|
| 128 |
+
"opcode": "control_create_clone_of",
|
| 129 |
+
"next": null,
|
| 130 |
+
"parent": null,
|
| 131 |
+
"inputs": {
|
| 132 |
+
"CLONE_OPTION": [
|
| 133 |
+
1,
|
| 134 |
+
"control_create_clone_of_menu"
|
| 135 |
+
]
|
| 136 |
+
},
|
| 137 |
+
"fields": {},
|
| 138 |
+
"shadow": false,
|
| 139 |
+
"topLevel": true,
|
| 140 |
+
"x": 648,
|
| 141 |
+
"y": 797
|
| 142 |
+
},
|
| 143 |
+
"control_create_clone_of_menu": {
|
| 144 |
+
"opcode": "control_create_clone_of_menu",
|
| 145 |
+
"next": null,
|
| 146 |
+
"parent": "control_create_clone_of",
|
| 147 |
+
"inputs": {},
|
| 148 |
+
"fields": {
|
| 149 |
+
"CLONE_OPTION": [
|
| 150 |
+
"_myself_",
|
| 151 |
+
null
|
| 152 |
+
]
|
| 153 |
+
},
|
| 154 |
+
"shadow": true,
|
| 155 |
+
"topLevel": false
|
| 156 |
+
},
|
| 157 |
+
"control_delete_this_clone": {
|
| 158 |
+
"opcode": "control_delete_this_clone",
|
| 159 |
+
"next": null,
|
| 160 |
+
"parent": null,
|
| 161 |
+
"inputs": {},
|
| 162 |
+
"fields": {},
|
| 163 |
+
"shadow": false,
|
| 164 |
+
"topLevel": true,
|
| 165 |
+
"x": 642,
|
| 166 |
+
"y": 914
|
| 167 |
+
}
|
| 168 |
+
}
|
blocks/classwise_blocks/data_block.json
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"data_setvariableto": {
|
| 3 |
+
"opcode": "data_setvariableto",
|
| 4 |
+
"next": null,
|
| 5 |
+
"parent": null,
|
| 6 |
+
"inputs": {
|
| 7 |
+
"VALUE": [
|
| 8 |
+
1,
|
| 9 |
+
[
|
| 10 |
+
10,
|
| 11 |
+
"0"
|
| 12 |
+
]
|
| 13 |
+
]
|
| 14 |
+
},
|
| 15 |
+
"fields": {
|
| 16 |
+
"VARIABLE": [
|
| 17 |
+
"my variable",
|
| 18 |
+
"`jEk@4|i[#Fk?(8x)AV.-my variable"
|
| 19 |
+
]
|
| 20 |
+
},
|
| 21 |
+
"shadow": false,
|
| 22 |
+
"topLevel": true,
|
| 23 |
+
"x": 348,
|
| 24 |
+
"y": 241
|
| 25 |
+
},
|
| 26 |
+
"data_changevariableby": {
|
| 27 |
+
"opcode": "data_changevariableby",
|
| 28 |
+
"next": null,
|
| 29 |
+
"parent": null,
|
| 30 |
+
"inputs": {
|
| 31 |
+
"VALUE": [
|
| 32 |
+
1,
|
| 33 |
+
[
|
| 34 |
+
4,
|
| 35 |
+
"1"
|
| 36 |
+
]
|
| 37 |
+
]
|
| 38 |
+
},
|
| 39 |
+
"fields": {
|
| 40 |
+
"VARIABLE": [
|
| 41 |
+
"my variable",
|
| 42 |
+
"`jEk@4|i[#Fk?(8x)AV.-my variable"
|
| 43 |
+
]
|
| 44 |
+
},
|
| 45 |
+
"shadow": false,
|
| 46 |
+
"topLevel": true,
|
| 47 |
+
"x": 313,
|
| 48 |
+
"y": 363
|
| 49 |
+
},
|
| 50 |
+
"data_showvariable": {
|
| 51 |
+
"opcode": "data_showvariable",
|
| 52 |
+
"next": null,
|
| 53 |
+
"parent": null,
|
| 54 |
+
"inputs": {},
|
| 55 |
+
"fields": {
|
| 56 |
+
"VARIABLE": [
|
| 57 |
+
"my variable",
|
| 58 |
+
"`jEk@4|i[#Fk?(8x)AV.-my variable"
|
| 59 |
+
]
|
| 60 |
+
},
|
| 61 |
+
"shadow": false,
|
| 62 |
+
"topLevel": true,
|
| 63 |
+
"x": 415,
|
| 64 |
+
"y": 473
|
| 65 |
+
},
|
| 66 |
+
"data_hidevariable": {
|
| 67 |
+
"opcode": "data_hidevariable",
|
| 68 |
+
"next": null,
|
| 69 |
+
"parent": null,
|
| 70 |
+
"inputs": {},
|
| 71 |
+
"fields": {
|
| 72 |
+
"VARIABLE": [
|
| 73 |
+
"my variable",
|
| 74 |
+
"`jEk@4|i[#Fk?(8x)AV.-my variable"
|
| 75 |
+
]
|
| 76 |
+
},
|
| 77 |
+
"shadow": false,
|
| 78 |
+
"topLevel": true,
|
| 79 |
+
"x": 319,
|
| 80 |
+
"y": 587
|
| 81 |
+
},
|
| 82 |
+
"data_addtolist": {
|
| 83 |
+
"opcode": "data_addtolist",
|
| 84 |
+
"next": null,
|
| 85 |
+
"parent": null,
|
| 86 |
+
"inputs": {
|
| 87 |
+
"ITEM": [
|
| 88 |
+
1,
|
| 89 |
+
[
|
| 90 |
+
10,
|
| 91 |
+
"thing"
|
| 92 |
+
]
|
| 93 |
+
]
|
| 94 |
+
},
|
| 95 |
+
"fields": {
|
| 96 |
+
"LIST": [
|
| 97 |
+
"MY_LIST",
|
| 98 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 99 |
+
]
|
| 100 |
+
},
|
| 101 |
+
"shadow": false,
|
| 102 |
+
"topLevel": true,
|
| 103 |
+
"x": 385,
|
| 104 |
+
"y": 109
|
| 105 |
+
},
|
| 106 |
+
"data_deleteoflist": {
|
| 107 |
+
"opcode": "data_deleteoflist",
|
| 108 |
+
"next": null,
|
| 109 |
+
"parent": null,
|
| 110 |
+
"inputs": {
|
| 111 |
+
"INDEX": [
|
| 112 |
+
1,
|
| 113 |
+
[
|
| 114 |
+
7,
|
| 115 |
+
"1"
|
| 116 |
+
]
|
| 117 |
+
]
|
| 118 |
+
},
|
| 119 |
+
"fields": {
|
| 120 |
+
"LIST": [
|
| 121 |
+
"MY_LIST",
|
| 122 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 123 |
+
]
|
| 124 |
+
},
|
| 125 |
+
"shadow": false,
|
| 126 |
+
"topLevel": true,
|
| 127 |
+
"x": 384,
|
| 128 |
+
"y": 244
|
| 129 |
+
},
|
| 130 |
+
"data_deletealloflist": {
|
| 131 |
+
"opcode": "data_deletealloflist",
|
| 132 |
+
"next": null,
|
| 133 |
+
"parent": null,
|
| 134 |
+
"inputs": {},
|
| 135 |
+
"fields": {
|
| 136 |
+
"LIST": [
|
| 137 |
+
"MY_LIST",
|
| 138 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 139 |
+
]
|
| 140 |
+
},
|
| 141 |
+
"shadow": false,
|
| 142 |
+
"topLevel": true,
|
| 143 |
+
"x": 387,
|
| 144 |
+
"y": 374
|
| 145 |
+
},
|
| 146 |
+
"data_insertatlist": {
|
| 147 |
+
"opcode": "data_insertatlist",
|
| 148 |
+
"next": null,
|
| 149 |
+
"parent": null,
|
| 150 |
+
"inputs": {
|
| 151 |
+
"ITEM": [
|
| 152 |
+
1,
|
| 153 |
+
[
|
| 154 |
+
10,
|
| 155 |
+
"thing"
|
| 156 |
+
]
|
| 157 |
+
],
|
| 158 |
+
"INDEX": [
|
| 159 |
+
1,
|
| 160 |
+
[
|
| 161 |
+
7,
|
| 162 |
+
"1"
|
| 163 |
+
]
|
| 164 |
+
]
|
| 165 |
+
},
|
| 166 |
+
"fields": {
|
| 167 |
+
"LIST": [
|
| 168 |
+
"MY_LIST",
|
| 169 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 170 |
+
]
|
| 171 |
+
},
|
| 172 |
+
"shadow": false,
|
| 173 |
+
"topLevel": true,
|
| 174 |
+
"x": 366,
|
| 175 |
+
"y": 527
|
| 176 |
+
},
|
| 177 |
+
"data_replaceitemoflist": {
|
| 178 |
+
"opcode": "data_replaceitemoflist",
|
| 179 |
+
"next": null,
|
| 180 |
+
"parent": null,
|
| 181 |
+
"inputs": {
|
| 182 |
+
"INDEX": [
|
| 183 |
+
1,
|
| 184 |
+
[
|
| 185 |
+
7,
|
| 186 |
+
"1"
|
| 187 |
+
]
|
| 188 |
+
],
|
| 189 |
+
"ITEM": [
|
| 190 |
+
1,
|
| 191 |
+
[
|
| 192 |
+
10,
|
| 193 |
+
"thing"
|
| 194 |
+
]
|
| 195 |
+
]
|
| 196 |
+
},
|
| 197 |
+
"fields": {
|
| 198 |
+
"LIST": [
|
| 199 |
+
"MY_LIST",
|
| 200 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 201 |
+
]
|
| 202 |
+
},
|
| 203 |
+
"shadow": false,
|
| 204 |
+
"topLevel": true,
|
| 205 |
+
"x": 365,
|
| 206 |
+
"y": 657
|
| 207 |
+
},
|
| 208 |
+
"data_itemoflist": {
|
| 209 |
+
"opcode": "data_itemoflist",
|
| 210 |
+
"next": null,
|
| 211 |
+
"parent": null,
|
| 212 |
+
"inputs": {
|
| 213 |
+
"INDEX": [
|
| 214 |
+
1,
|
| 215 |
+
[
|
| 216 |
+
7,
|
| 217 |
+
"1"
|
| 218 |
+
]
|
| 219 |
+
]
|
| 220 |
+
},
|
| 221 |
+
"fields": {
|
| 222 |
+
"LIST": [
|
| 223 |
+
"MY_LIST",
|
| 224 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 225 |
+
]
|
| 226 |
+
},
|
| 227 |
+
"shadow": false,
|
| 228 |
+
"topLevel": true,
|
| 229 |
+
"x": 862,
|
| 230 |
+
"y": 117
|
| 231 |
+
},
|
| 232 |
+
"data_itemnumoflist": {
|
| 233 |
+
"opcode": "data_itemnumoflist",
|
| 234 |
+
"next": null,
|
| 235 |
+
"parent": null,
|
| 236 |
+
"inputs": {
|
| 237 |
+
"ITEM": [
|
| 238 |
+
1,
|
| 239 |
+
[
|
| 240 |
+
10,
|
| 241 |
+
"thing"
|
| 242 |
+
]
|
| 243 |
+
]
|
| 244 |
+
},
|
| 245 |
+
"fields": {
|
| 246 |
+
"LIST": [
|
| 247 |
+
"MY_LIST",
|
| 248 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 249 |
+
]
|
| 250 |
+
},
|
| 251 |
+
"shadow": false,
|
| 252 |
+
"topLevel": true,
|
| 253 |
+
"x": 883,
|
| 254 |
+
"y": 238
|
| 255 |
+
},
|
| 256 |
+
"data_lengthoflist": {
|
| 257 |
+
"opcode": "data_lengthoflist",
|
| 258 |
+
"next": null,
|
| 259 |
+
"parent": null,
|
| 260 |
+
"inputs": {},
|
| 261 |
+
"fields": {
|
| 262 |
+
"LIST": [
|
| 263 |
+
"MY_LIST",
|
| 264 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 265 |
+
]
|
| 266 |
+
},
|
| 267 |
+
"shadow": false,
|
| 268 |
+
"topLevel": true,
|
| 269 |
+
"x": 876,
|
| 270 |
+
"y": 342
|
| 271 |
+
},
|
| 272 |
+
"data_listcontainsitem": {
|
| 273 |
+
"opcode": "data_listcontainsitem",
|
| 274 |
+
"next": null,
|
| 275 |
+
"parent": null,
|
| 276 |
+
"inputs": {
|
| 277 |
+
"ITEM": [
|
| 278 |
+
1,
|
| 279 |
+
[
|
| 280 |
+
10,
|
| 281 |
+
"thing"
|
| 282 |
+
]
|
| 283 |
+
]
|
| 284 |
+
},
|
| 285 |
+
"fields": {
|
| 286 |
+
"LIST": [
|
| 287 |
+
"MY_LIST",
|
| 288 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 289 |
+
]
|
| 290 |
+
},
|
| 291 |
+
"shadow": false,
|
| 292 |
+
"topLevel": true,
|
| 293 |
+
"x": 871,
|
| 294 |
+
"y": 463
|
| 295 |
+
},
|
| 296 |
+
"data_showlist": {
|
| 297 |
+
"opcode": "data_showlist",
|
| 298 |
+
"next": null,
|
| 299 |
+
"parent": null,
|
| 300 |
+
"inputs": {},
|
| 301 |
+
"fields": {
|
| 302 |
+
"LIST": [
|
| 303 |
+
"MY_LIST",
|
| 304 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 305 |
+
]
|
| 306 |
+
},
|
| 307 |
+
"shadow": false,
|
| 308 |
+
"topLevel": true,
|
| 309 |
+
"x": 931,
|
| 310 |
+
"y": 563
|
| 311 |
+
},
|
| 312 |
+
"data_hidelist": {
|
| 313 |
+
"opcode": "data_hidelist",
|
| 314 |
+
"next": null,
|
| 315 |
+
"parent": null,
|
| 316 |
+
"inputs": {},
|
| 317 |
+
"fields": {
|
| 318 |
+
"LIST": [
|
| 319 |
+
"MY_LIST",
|
| 320 |
+
"o6`kIhtT{xWH+rX(5d,A"
|
| 321 |
+
]
|
| 322 |
+
},
|
| 323 |
+
"shadow": false,
|
| 324 |
+
"topLevel": true,
|
| 325 |
+
"x": 962,
|
| 326 |
+
"y": 716
|
| 327 |
+
}
|
| 328 |
+
}
|
blocks/classwise_blocks/event_block.json
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"event_whenflagclicked": {
|
| 3 |
+
"opcode": "event_whenflagclicked",
|
| 4 |
+
"next": null,
|
| 5 |
+
"parent": null,
|
| 6 |
+
"inputs": {},
|
| 7 |
+
"fields": {},
|
| 8 |
+
"shadow": false,
|
| 9 |
+
"topLevel": true,
|
| 10 |
+
"x": 166,
|
| 11 |
+
"y": -422
|
| 12 |
+
},
|
| 13 |
+
"event_whenkeypressed": {
|
| 14 |
+
"opcode": "event_whenkeypressed",
|
| 15 |
+
"next": null,
|
| 16 |
+
"parent": null,
|
| 17 |
+
"inputs": {},
|
| 18 |
+
"fields": {
|
| 19 |
+
"KEY_OPTION": [
|
| 20 |
+
"space",
|
| 21 |
+
null
|
| 22 |
+
]
|
| 23 |
+
},
|
| 24 |
+
"shadow": false,
|
| 25 |
+
"topLevel": true,
|
| 26 |
+
"x": 151,
|
| 27 |
+
"y": -329
|
| 28 |
+
},
|
| 29 |
+
"event_whenthisspriteclicked": {
|
| 30 |
+
"opcode": "event_whenthisspriteclicked",
|
| 31 |
+
"next": null,
|
| 32 |
+
"parent": null,
|
| 33 |
+
"inputs": {},
|
| 34 |
+
"fields": {},
|
| 35 |
+
"shadow": false,
|
| 36 |
+
"topLevel": true,
|
| 37 |
+
"x": 156,
|
| 38 |
+
"y": -223
|
| 39 |
+
},
|
| 40 |
+
"event_whenbackdropswitchesto": {
|
| 41 |
+
"opcode": "event_whenbackdropswitchesto",
|
| 42 |
+
"next": null,
|
| 43 |
+
"parent": null,
|
| 44 |
+
"inputs": {},
|
| 45 |
+
"fields": {
|
| 46 |
+
"BACKDROP": [
|
| 47 |
+
"backdrop1",
|
| 48 |
+
null
|
| 49 |
+
]
|
| 50 |
+
},
|
| 51 |
+
"shadow": false,
|
| 52 |
+
"topLevel": true,
|
| 53 |
+
"x": 148,
|
| 54 |
+
"y": -101
|
| 55 |
+
},
|
| 56 |
+
"event_whengreaterthan": {
|
| 57 |
+
"opcode": "event_whengreaterthan",
|
| 58 |
+
"next": null,
|
| 59 |
+
"parent": null,
|
| 60 |
+
"inputs": {
|
| 61 |
+
"VALUE": [
|
| 62 |
+
1,
|
| 63 |
+
[
|
| 64 |
+
4,
|
| 65 |
+
"10"
|
| 66 |
+
]
|
| 67 |
+
]
|
| 68 |
+
},
|
| 69 |
+
"fields": {
|
| 70 |
+
"WHENGREATERTHANMENU": [
|
| 71 |
+
"LOUDNESS",
|
| 72 |
+
null
|
| 73 |
+
]
|
| 74 |
+
},
|
| 75 |
+
"shadow": false,
|
| 76 |
+
"topLevel": true,
|
| 77 |
+
"x": 150,
|
| 78 |
+
"y": 10
|
| 79 |
+
},
|
| 80 |
+
"event_whenbroadcastreceived": {
|
| 81 |
+
"opcode": "event_whenbroadcastreceived",
|
| 82 |
+
"next": null,
|
| 83 |
+
"parent": null,
|
| 84 |
+
"inputs": {},
|
| 85 |
+
"fields": {
|
| 86 |
+
"BROADCAST_OPTION": [
|
| 87 |
+
"message1",
|
| 88 |
+
"5O!nei;S$!c!=hCT}0:a"
|
| 89 |
+
]
|
| 90 |
+
},
|
| 91 |
+
"shadow": false,
|
| 92 |
+
"topLevel": true,
|
| 93 |
+
"x": 141,
|
| 94 |
+
"y": 118
|
| 95 |
+
},
|
| 96 |
+
"event_broadcast": {
|
| 97 |
+
"opcode": "event_broadcast",
|
| 98 |
+
"next": null,
|
| 99 |
+
"parent": null,
|
| 100 |
+
"inputs": {
|
| 101 |
+
"BROADCAST_INPUT": [
|
| 102 |
+
1,
|
| 103 |
+
[
|
| 104 |
+
11,
|
| 105 |
+
"message1",
|
| 106 |
+
"5O!nei;S$!c!=hCT}0:a"
|
| 107 |
+
]
|
| 108 |
+
]
|
| 109 |
+
},
|
| 110 |
+
"fields": {},
|
| 111 |
+
"shadow": false,
|
| 112 |
+
"topLevel": true,
|
| 113 |
+
"x": 151,
|
| 114 |
+
"y": 229
|
| 115 |
+
},
|
| 116 |
+
"event_broadcastandwait": {
|
| 117 |
+
"opcode": "event_broadcastandwait",
|
| 118 |
+
"next": null,
|
| 119 |
+
"parent": null,
|
| 120 |
+
"inputs": {
|
| 121 |
+
"BROADCAST_INPUT": [
|
| 122 |
+
1,
|
| 123 |
+
[
|
| 124 |
+
11,
|
| 125 |
+
"message1",
|
| 126 |
+
"5O!nei;S$!c!=hCT}0:a"
|
| 127 |
+
]
|
| 128 |
+
]
|
| 129 |
+
},
|
| 130 |
+
"fields": {},
|
| 131 |
+
"shadow": false,
|
| 132 |
+
"topLevel": true,
|
| 133 |
+
"x": 157,
|
| 134 |
+
"y": 340
|
| 135 |
+
}
|
| 136 |
+
}
|
blocks/classwise_blocks/look_block.json
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"looks_sayforsecs": {
|
| 3 |
+
"opcode": "looks_sayforsecs",
|
| 4 |
+
"next": null,
|
| 5 |
+
"parent": null,
|
| 6 |
+
"inputs": {
|
| 7 |
+
"MESSAGE": [
|
| 8 |
+
1,
|
| 9 |
+
[
|
| 10 |
+
10,
|
| 11 |
+
"Hello!"
|
| 12 |
+
]
|
| 13 |
+
],
|
| 14 |
+
"SECS": [
|
| 15 |
+
1,
|
| 16 |
+
[
|
| 17 |
+
4,
|
| 18 |
+
"2"
|
| 19 |
+
]
|
| 20 |
+
]
|
| 21 |
+
},
|
| 22 |
+
"fields": {},
|
| 23 |
+
"shadow": false,
|
| 24 |
+
"topLevel": true,
|
| 25 |
+
"x": 408,
|
| 26 |
+
"y": 91
|
| 27 |
+
},
|
| 28 |
+
"looks_say": {
|
| 29 |
+
"opcode": "looks_say",
|
| 30 |
+
"next": null,
|
| 31 |
+
"parent": null,
|
| 32 |
+
"inputs": {
|
| 33 |
+
"MESSAGE": [
|
| 34 |
+
1,
|
| 35 |
+
[
|
| 36 |
+
10,
|
| 37 |
+
"Hello!"
|
| 38 |
+
]
|
| 39 |
+
]
|
| 40 |
+
},
|
| 41 |
+
"fields": {},
|
| 42 |
+
"shadow": false,
|
| 43 |
+
"topLevel": true,
|
| 44 |
+
"x": 413,
|
| 45 |
+
"y": 213
|
| 46 |
+
},
|
| 47 |
+
"looks_thinkforsecs": {
|
| 48 |
+
"opcode": "looks_thinkforsecs",
|
| 49 |
+
"next": null,
|
| 50 |
+
"parent": null,
|
| 51 |
+
"inputs": {
|
| 52 |
+
"MESSAGE": [
|
| 53 |
+
1,
|
| 54 |
+
[
|
| 55 |
+
10,
|
| 56 |
+
"Hmm..."
|
| 57 |
+
]
|
| 58 |
+
],
|
| 59 |
+
"SECS": [
|
| 60 |
+
1,
|
| 61 |
+
[
|
| 62 |
+
4,
|
| 63 |
+
"2"
|
| 64 |
+
]
|
| 65 |
+
]
|
| 66 |
+
},
|
| 67 |
+
"fields": {},
|
| 68 |
+
"shadow": false,
|
| 69 |
+
"topLevel": true,
|
| 70 |
+
"x": 413,
|
| 71 |
+
"y": 317
|
| 72 |
+
},
|
| 73 |
+
"looks_think": {
|
| 74 |
+
"opcode": "looks_think",
|
| 75 |
+
"next": null,
|
| 76 |
+
"parent": null,
|
| 77 |
+
"inputs": {
|
| 78 |
+
"MESSAGE": [
|
| 79 |
+
1,
|
| 80 |
+
[
|
| 81 |
+
10,
|
| 82 |
+
"Hmm..."
|
| 83 |
+
]
|
| 84 |
+
]
|
| 85 |
+
},
|
| 86 |
+
"fields": {},
|
| 87 |
+
"shadow": false,
|
| 88 |
+
"topLevel": true,
|
| 89 |
+
"x": 412,
|
| 90 |
+
"y": 432
|
| 91 |
+
},
|
| 92 |
+
"looks_switchcostumeto": {
|
| 93 |
+
"opcode": "looks_switchcostumeto",
|
| 94 |
+
"next": null,
|
| 95 |
+
"parent": null,
|
| 96 |
+
"inputs": {
|
| 97 |
+
"COSTUME": [
|
| 98 |
+
1,
|
| 99 |
+
"looks_costume"
|
| 100 |
+
]
|
| 101 |
+
},
|
| 102 |
+
"fields": {},
|
| 103 |
+
"shadow": false,
|
| 104 |
+
"topLevel": true,
|
| 105 |
+
"x": 411,
|
| 106 |
+
"y": 555
|
| 107 |
+
},
|
| 108 |
+
"looks_costume": {
|
| 109 |
+
"opcode": "looks_costume",
|
| 110 |
+
"next": null,
|
| 111 |
+
"parent": "looks_switchcostumeto",
|
| 112 |
+
"inputs": {},
|
| 113 |
+
"fields": {
|
| 114 |
+
"COSTUME": [
|
| 115 |
+
"costume2",
|
| 116 |
+
null
|
| 117 |
+
]
|
| 118 |
+
},
|
| 119 |
+
"shadow": true,
|
| 120 |
+
"topLevel": false
|
| 121 |
+
},
|
| 122 |
+
"looks_nextcostume": {
|
| 123 |
+
"opcode": "looks_nextcostume",
|
| 124 |
+
"next": null,
|
| 125 |
+
"parent": null,
|
| 126 |
+
"inputs": {},
|
| 127 |
+
"fields": {},
|
| 128 |
+
"shadow": false,
|
| 129 |
+
"topLevel": true,
|
| 130 |
+
"x": 419,
|
| 131 |
+
"y": 687
|
| 132 |
+
},
|
| 133 |
+
"looks_switchbackdropto": {
|
| 134 |
+
"opcode": "looks_switchbackdropto",
|
| 135 |
+
"next": null,
|
| 136 |
+
"parent": null,
|
| 137 |
+
"inputs": {
|
| 138 |
+
"BACKDROP": [
|
| 139 |
+
1,
|
| 140 |
+
"looks_backdrops"
|
| 141 |
+
]
|
| 142 |
+
},
|
| 143 |
+
"fields": {},
|
| 144 |
+
"shadow": false,
|
| 145 |
+
"topLevel": true,
|
| 146 |
+
"x": 901,
|
| 147 |
+
"y": 91
|
| 148 |
+
},
|
| 149 |
+
"looks_backdrops": {
|
| 150 |
+
"opcode": "looks_backdrops",
|
| 151 |
+
"next": null,
|
| 152 |
+
"parent": "looks_switchbackdropto",
|
| 153 |
+
"inputs": {},
|
| 154 |
+
"fields": {
|
| 155 |
+
"BACKDROP": [
|
| 156 |
+
"backdrop1",
|
| 157 |
+
null
|
| 158 |
+
]
|
| 159 |
+
},
|
| 160 |
+
"shadow": true,
|
| 161 |
+
"topLevel": false
|
| 162 |
+
},
|
| 163 |
+
"looks_changesizeby": {
|
| 164 |
+
"opcode": "looks_changesizeby",
|
| 165 |
+
"next": null,
|
| 166 |
+
"parent": null,
|
| 167 |
+
"inputs": {
|
| 168 |
+
"CHANGE": [
|
| 169 |
+
1,
|
| 170 |
+
[
|
| 171 |
+
4,
|
| 172 |
+
"10"
|
| 173 |
+
]
|
| 174 |
+
]
|
| 175 |
+
},
|
| 176 |
+
"fields": {},
|
| 177 |
+
"shadow": false,
|
| 178 |
+
"topLevel": true,
|
| 179 |
+
"x": 895,
|
| 180 |
+
"y": 192
|
| 181 |
+
},
|
| 182 |
+
"looks_setsizeto": {
|
| 183 |
+
"opcode": "looks_setsizeto",
|
| 184 |
+
"next": null,
|
| 185 |
+
"parent": null,
|
| 186 |
+
"inputs": {
|
| 187 |
+
"SIZE": [
|
| 188 |
+
1,
|
| 189 |
+
[
|
| 190 |
+
4,
|
| 191 |
+
"100"
|
| 192 |
+
]
|
| 193 |
+
]
|
| 194 |
+
},
|
| 195 |
+
"fields": {},
|
| 196 |
+
"shadow": false,
|
| 197 |
+
"topLevel": true,
|
| 198 |
+
"x": 896,
|
| 199 |
+
"y": 303
|
| 200 |
+
},
|
| 201 |
+
"looks_changeeffectby": {
|
| 202 |
+
"opcode": "looks_changeeffectby",
|
| 203 |
+
"next": null,
|
| 204 |
+
"parent": null,
|
| 205 |
+
"inputs": {
|
| 206 |
+
"CHANGE": [
|
| 207 |
+
1,
|
| 208 |
+
[
|
| 209 |
+
4,
|
| 210 |
+
"25"
|
| 211 |
+
]
|
| 212 |
+
]
|
| 213 |
+
},
|
| 214 |
+
"fields": {
|
| 215 |
+
"EFFECT": [
|
| 216 |
+
"COLOR",
|
| 217 |
+
null
|
| 218 |
+
]
|
| 219 |
+
},
|
| 220 |
+
"shadow": false,
|
| 221 |
+
"topLevel": true,
|
| 222 |
+
"x": 892,
|
| 223 |
+
"y": 416
|
| 224 |
+
},
|
| 225 |
+
"looks_seteffectto": {
|
| 226 |
+
"opcode": "looks_seteffectto",
|
| 227 |
+
"next": null,
|
| 228 |
+
"parent": null,
|
| 229 |
+
"inputs": {
|
| 230 |
+
"VALUE": [
|
| 231 |
+
1,
|
| 232 |
+
[
|
| 233 |
+
4,
|
| 234 |
+
"0"
|
| 235 |
+
]
|
| 236 |
+
]
|
| 237 |
+
},
|
| 238 |
+
"fields": {
|
| 239 |
+
"EFFECT": [
|
| 240 |
+
"COLOR",
|
| 241 |
+
null
|
| 242 |
+
]
|
| 243 |
+
},
|
| 244 |
+
"shadow": false,
|
| 245 |
+
"topLevel": true,
|
| 246 |
+
"x": 902,
|
| 247 |
+
"y": 527
|
| 248 |
+
},
|
| 249 |
+
"looks_cleargraphiceffects": {
|
| 250 |
+
"opcode": "looks_cleargraphiceffects",
|
| 251 |
+
"next": null,
|
| 252 |
+
"parent": null,
|
| 253 |
+
"inputs": {},
|
| 254 |
+
"fields": {},
|
| 255 |
+
"shadow": false,
|
| 256 |
+
"topLevel": true,
|
| 257 |
+
"x": 902,
|
| 258 |
+
"y": 638
|
| 259 |
+
},
|
| 260 |
+
"looks_show": {
|
| 261 |
+
"opcode": "looks_show",
|
| 262 |
+
"next": null,
|
| 263 |
+
"parent": null,
|
| 264 |
+
"inputs": {},
|
| 265 |
+
"fields": {},
|
| 266 |
+
"shadow": false,
|
| 267 |
+
"topLevel": true,
|
| 268 |
+
"x": 908,
|
| 269 |
+
"y": 758
|
| 270 |
+
},
|
| 271 |
+
"looks_hide": {
|
| 272 |
+
"opcode": "looks_hide",
|
| 273 |
+
"next": null,
|
| 274 |
+
"parent": null,
|
| 275 |
+
"inputs": {},
|
| 276 |
+
"fields": {},
|
| 277 |
+
"shadow": false,
|
| 278 |
+
"topLevel": true,
|
| 279 |
+
"x": 455,
|
| 280 |
+
"y": 861
|
| 281 |
+
},
|
| 282 |
+
"looks_gotofrontback": {
|
| 283 |
+
"opcode": "looks_gotofrontback",
|
| 284 |
+
"next": null,
|
| 285 |
+
"parent": null,
|
| 286 |
+
"inputs": {},
|
| 287 |
+
"fields": {
|
| 288 |
+
"FRONT_BACK": [
|
| 289 |
+
"front",
|
| 290 |
+
null
|
| 291 |
+
]
|
| 292 |
+
},
|
| 293 |
+
"shadow": false,
|
| 294 |
+
"topLevel": true,
|
| 295 |
+
"x": 853,
|
| 296 |
+
"y": 878
|
| 297 |
+
},
|
| 298 |
+
"looks_goforwardbackwardlayers": {
|
| 299 |
+
"opcode": "looks_goforwardbackwardlayers",
|
| 300 |
+
"next": null,
|
| 301 |
+
"parent": null,
|
| 302 |
+
"inputs": {
|
| 303 |
+
"NUM": [
|
| 304 |
+
1,
|
| 305 |
+
[
|
| 306 |
+
7,
|
| 307 |
+
"1"
|
| 308 |
+
]
|
| 309 |
+
]
|
| 310 |
+
},
|
| 311 |
+
"fields": {
|
| 312 |
+
"FORWARD_BACKWARD": [
|
| 313 |
+
"forward",
|
| 314 |
+
null
|
| 315 |
+
]
|
| 316 |
+
},
|
| 317 |
+
"shadow": false,
|
| 318 |
+
"topLevel": true,
|
| 319 |
+
"x": 851,
|
| 320 |
+
"y": 999
|
| 321 |
+
},
|
| 322 |
+
"looks_costumenumbername": {
|
| 323 |
+
"opcode": "looks_costumenumbername",
|
| 324 |
+
"next": null,
|
| 325 |
+
"parent": null,
|
| 326 |
+
"inputs": {},
|
| 327 |
+
"fields": {
|
| 328 |
+
"NUMBER_NAME": [
|
| 329 |
+
"number",
|
| 330 |
+
null
|
| 331 |
+
]
|
| 332 |
+
},
|
| 333 |
+
"shadow": false,
|
| 334 |
+
"topLevel": true,
|
| 335 |
+
"x": 458,
|
| 336 |
+
"y": 1007
|
| 337 |
+
},
|
| 338 |
+
"looks_backdropnumbername": {
|
| 339 |
+
"opcode": "looks_backdropnumbername",
|
| 340 |
+
"next": null,
|
| 341 |
+
"parent": null,
|
| 342 |
+
"inputs": {},
|
| 343 |
+
"fields": {
|
| 344 |
+
"NUMBER_NAME": [
|
| 345 |
+
"number",
|
| 346 |
+
null
|
| 347 |
+
]
|
| 348 |
+
},
|
| 349 |
+
"shadow": false,
|
| 350 |
+
"topLevel": true,
|
| 351 |
+
"x": 1242,
|
| 352 |
+
"y": 753
|
| 353 |
+
},
|
| 354 |
+
"looks_size": {
|
| 355 |
+
"opcode": "looks_size",
|
| 356 |
+
"next": null,
|
| 357 |
+
"parent": null,
|
| 358 |
+
"inputs": {},
|
| 359 |
+
"fields": {},
|
| 360 |
+
"shadow": false,
|
| 361 |
+
"topLevel": true,
|
| 362 |
+
"x": 1249,
|
| 363 |
+
"y": 876
|
| 364 |
+
}
|
| 365 |
+
}
|
blocks/classwise_blocks/motion_block.json
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"motion_movesteps": {
|
| 3 |
+
"opcode": "motion_movesteps",
|
| 4 |
+
"next": null,
|
| 5 |
+
"parent": null,
|
| 6 |
+
"inputs": {
|
| 7 |
+
"STEPS": [
|
| 8 |
+
1,
|
| 9 |
+
[
|
| 10 |
+
4,
|
| 11 |
+
"10"
|
| 12 |
+
]
|
| 13 |
+
]
|
| 14 |
+
},
|
| 15 |
+
"fields": {},
|
| 16 |
+
"shadow": false,
|
| 17 |
+
"topLevel": true,
|
| 18 |
+
"x": 464,
|
| 19 |
+
"y": -416
|
| 20 |
+
},
|
| 21 |
+
"motion_turnright": {
|
| 22 |
+
"opcode": "motion_turnright",
|
| 23 |
+
"next": null,
|
| 24 |
+
"parent": null,
|
| 25 |
+
"inputs": {
|
| 26 |
+
"DEGREES": [
|
| 27 |
+
1,
|
| 28 |
+
[
|
| 29 |
+
4,
|
| 30 |
+
"15"
|
| 31 |
+
]
|
| 32 |
+
]
|
| 33 |
+
},
|
| 34 |
+
"fields": {},
|
| 35 |
+
"shadow": false,
|
| 36 |
+
"topLevel": true,
|
| 37 |
+
"x": 467,
|
| 38 |
+
"y": -316
|
| 39 |
+
},
|
| 40 |
+
"motion_turnleft": {
|
| 41 |
+
"opcode": "motion_turnleft",
|
| 42 |
+
"next": null,
|
| 43 |
+
"parent": null,
|
| 44 |
+
"inputs": {
|
| 45 |
+
"DEGREES": [
|
| 46 |
+
1,
|
| 47 |
+
[
|
| 48 |
+
4,
|
| 49 |
+
"15"
|
| 50 |
+
]
|
| 51 |
+
]
|
| 52 |
+
},
|
| 53 |
+
"fields": {},
|
| 54 |
+
"shadow": false,
|
| 55 |
+
"topLevel": true,
|
| 56 |
+
"x": 464,
|
| 57 |
+
"y": -210
|
| 58 |
+
},
|
| 59 |
+
"motion_goto": {
|
| 60 |
+
"opcode": "motion_goto",
|
| 61 |
+
"next": null,
|
| 62 |
+
"parent": null,
|
| 63 |
+
"inputs": {
|
| 64 |
+
"TO": [
|
| 65 |
+
1,
|
| 66 |
+
"motion_goto_menu"
|
| 67 |
+
]
|
| 68 |
+
},
|
| 69 |
+
"fields": {},
|
| 70 |
+
"shadow": false,
|
| 71 |
+
"topLevel": true,
|
| 72 |
+
"x": 465,
|
| 73 |
+
"y": -95
|
| 74 |
+
},
|
| 75 |
+
"motion_goto_menu": {
|
| 76 |
+
"opcode": "motion_goto_menu",
|
| 77 |
+
"next": null,
|
| 78 |
+
"parent": "motion_goto",
|
| 79 |
+
"inputs": {},
|
| 80 |
+
"fields": {
|
| 81 |
+
"TO": [
|
| 82 |
+
"_random_",
|
| 83 |
+
null
|
| 84 |
+
]
|
| 85 |
+
},
|
| 86 |
+
"shadow": true,
|
| 87 |
+
"topLevel": false
|
| 88 |
+
},
|
| 89 |
+
"motion_gotoxy": {
|
| 90 |
+
"opcode": "motion_gotoxy",
|
| 91 |
+
"next": null,
|
| 92 |
+
"parent": null,
|
| 93 |
+
"inputs": {
|
| 94 |
+
"X": [
|
| 95 |
+
1,
|
| 96 |
+
[
|
| 97 |
+
4,
|
| 98 |
+
"0"
|
| 99 |
+
]
|
| 100 |
+
],
|
| 101 |
+
"Y": [
|
| 102 |
+
1,
|
| 103 |
+
[
|
| 104 |
+
4,
|
| 105 |
+
"0"
|
| 106 |
+
]
|
| 107 |
+
]
|
| 108 |
+
},
|
| 109 |
+
"fields": {},
|
| 110 |
+
"shadow": false,
|
| 111 |
+
"topLevel": true,
|
| 112 |
+
"x": 468,
|
| 113 |
+
"y": 12
|
| 114 |
+
},
|
| 115 |
+
"motion_glideto": {
|
| 116 |
+
"opcode": "motion_glideto",
|
| 117 |
+
"next": null,
|
| 118 |
+
"parent": null,
|
| 119 |
+
"inputs": {
|
| 120 |
+
"SECS": [
|
| 121 |
+
1,
|
| 122 |
+
[
|
| 123 |
+
4,
|
| 124 |
+
"1"
|
| 125 |
+
]
|
| 126 |
+
],
|
| 127 |
+
"TO": [
|
| 128 |
+
1,
|
| 129 |
+
"motion_glideto_menu"
|
| 130 |
+
]
|
| 131 |
+
},
|
| 132 |
+
"fields": {},
|
| 133 |
+
"shadow": false,
|
| 134 |
+
"topLevel": true,
|
| 135 |
+
"x": 470,
|
| 136 |
+
"y": 129
|
| 137 |
+
},
|
| 138 |
+
"motion_glideto_menu": {
|
| 139 |
+
"opcode": "motion_glideto_menu",
|
| 140 |
+
"next": null,
|
| 141 |
+
"parent": "motion_glideto",
|
| 142 |
+
"inputs": {},
|
| 143 |
+
"fields": {
|
| 144 |
+
"TO": [
|
| 145 |
+
"_random_",
|
| 146 |
+
null
|
| 147 |
+
]
|
| 148 |
+
},
|
| 149 |
+
"shadow": true,
|
| 150 |
+
"topLevel": false
|
| 151 |
+
},
|
| 152 |
+
"motion_glidesecstoxy": {
|
| 153 |
+
"opcode": "motion_glidesecstoxy",
|
| 154 |
+
"next": null,
|
| 155 |
+
"parent": null,
|
| 156 |
+
"inputs": {
|
| 157 |
+
"SECS": [
|
| 158 |
+
1,
|
| 159 |
+
[
|
| 160 |
+
4,
|
| 161 |
+
"1"
|
| 162 |
+
]
|
| 163 |
+
],
|
| 164 |
+
"X": [
|
| 165 |
+
1,
|
| 166 |
+
[
|
| 167 |
+
4,
|
| 168 |
+
"0"
|
| 169 |
+
]
|
| 170 |
+
],
|
| 171 |
+
"Y": [
|
| 172 |
+
1,
|
| 173 |
+
[
|
| 174 |
+
4,
|
| 175 |
+
"0"
|
| 176 |
+
]
|
| 177 |
+
]
|
| 178 |
+
},
|
| 179 |
+
"fields": {},
|
| 180 |
+
"shadow": false,
|
| 181 |
+
"topLevel": true,
|
| 182 |
+
"x": 476,
|
| 183 |
+
"y": 239
|
| 184 |
+
},
|
| 185 |
+
"motion_pointindirection": {
|
| 186 |
+
"opcode": "motion_pointindirection",
|
| 187 |
+
"next": null,
|
| 188 |
+
"parent": null,
|
| 189 |
+
"inputs": {
|
| 190 |
+
"DIRECTION": [
|
| 191 |
+
1,
|
| 192 |
+
[
|
| 193 |
+
8,
|
| 194 |
+
"90"
|
| 195 |
+
]
|
| 196 |
+
]
|
| 197 |
+
},
|
| 198 |
+
"fields": {},
|
| 199 |
+
"shadow": false,
|
| 200 |
+
"topLevel": true,
|
| 201 |
+
"x": 493,
|
| 202 |
+
"y": 361
|
| 203 |
+
},
|
| 204 |
+
"motion_pointtowards": {
|
| 205 |
+
"opcode": "motion_pointtowards",
|
| 206 |
+
"next": null,
|
| 207 |
+
"parent": null,
|
| 208 |
+
"inputs": {
|
| 209 |
+
"TOWARDS": [
|
| 210 |
+
1,
|
| 211 |
+
"6xQl1pPk%9E~Znhm*:ng"
|
| 212 |
+
]
|
| 213 |
+
},
|
| 214 |
+
"fields": {},
|
| 215 |
+
"shadow": false,
|
| 216 |
+
"topLevel": true,
|
| 217 |
+
"x": 492,
|
| 218 |
+
"y": 463
|
| 219 |
+
},
|
| 220 |
+
"motion_pointtowards_menu": {
|
| 221 |
+
"opcode": "motion_pointtowards_menu",
|
| 222 |
+
"next": null,
|
| 223 |
+
"parent": "Ucm$YBs*^9GFTGXCbal@",
|
| 224 |
+
"inputs": {},
|
| 225 |
+
"fields": {
|
| 226 |
+
"TOWARDS": [
|
| 227 |
+
"_mouse_",
|
| 228 |
+
null
|
| 229 |
+
]
|
| 230 |
+
},
|
| 231 |
+
"shadow": true,
|
| 232 |
+
"topLevel": false
|
| 233 |
+
},
|
| 234 |
+
"motion_changexby": {
|
| 235 |
+
"opcode": "motion_changexby",
|
| 236 |
+
"next": null,
|
| 237 |
+
"parent": null,
|
| 238 |
+
"inputs": {
|
| 239 |
+
"DX": [
|
| 240 |
+
1,
|
| 241 |
+
[
|
| 242 |
+
4,
|
| 243 |
+
"10"
|
| 244 |
+
]
|
| 245 |
+
]
|
| 246 |
+
},
|
| 247 |
+
"fields": {},
|
| 248 |
+
"shadow": false,
|
| 249 |
+
"topLevel": true,
|
| 250 |
+
"x": 851,
|
| 251 |
+
"y": -409
|
| 252 |
+
},
|
| 253 |
+
"motion_setx": {
|
| 254 |
+
"opcode": "motion_setx",
|
| 255 |
+
"next": null,
|
| 256 |
+
"parent": null,
|
| 257 |
+
"inputs": {
|
| 258 |
+
"X": [
|
| 259 |
+
1,
|
| 260 |
+
[
|
| 261 |
+
4,
|
| 262 |
+
"0"
|
| 263 |
+
]
|
| 264 |
+
]
|
| 265 |
+
},
|
| 266 |
+
"fields": {},
|
| 267 |
+
"shadow": false,
|
| 268 |
+
"topLevel": true,
|
| 269 |
+
"x": 864,
|
| 270 |
+
"y": -194
|
| 271 |
+
},
|
| 272 |
+
"motion_changeyby": {
|
| 273 |
+
"opcode": "motion_changeyby",
|
| 274 |
+
"next": null,
|
| 275 |
+
"parent": null,
|
| 276 |
+
"inputs": {
|
| 277 |
+
"DY": [
|
| 278 |
+
1,
|
| 279 |
+
[
|
| 280 |
+
4,
|
| 281 |
+
"10"
|
| 282 |
+
]
|
| 283 |
+
]
|
| 284 |
+
},
|
| 285 |
+
"fields": {},
|
| 286 |
+
"shadow": false,
|
| 287 |
+
"topLevel": true,
|
| 288 |
+
"x": 861,
|
| 289 |
+
"y": -61
|
| 290 |
+
},
|
| 291 |
+
"motion_sety": {
|
| 292 |
+
"opcode": "motion_sety",
|
| 293 |
+
"next": null,
|
| 294 |
+
"parent": null,
|
| 295 |
+
"inputs": {
|
| 296 |
+
"Y": [
|
| 297 |
+
1,
|
| 298 |
+
[
|
| 299 |
+
4,
|
| 300 |
+
"0"
|
| 301 |
+
]
|
| 302 |
+
]
|
| 303 |
+
},
|
| 304 |
+
"fields": {},
|
| 305 |
+
"shadow": false,
|
| 306 |
+
"topLevel": true,
|
| 307 |
+
"x": 864,
|
| 308 |
+
"y": 66
|
| 309 |
+
},
|
| 310 |
+
"motion_ifonedgebounce": {
|
| 311 |
+
"opcode": "motion_ifonedgebounce",
|
| 312 |
+
"next": null,
|
| 313 |
+
"parent": null,
|
| 314 |
+
"inputs": {},
|
| 315 |
+
"fields": {},
|
| 316 |
+
"shadow": false,
|
| 317 |
+
"topLevel": true,
|
| 318 |
+
"x": 1131,
|
| 319 |
+
"y": -397
|
| 320 |
+
},
|
| 321 |
+
"motion_setrotationstyle": {
|
| 322 |
+
"opcode": "motion_setrotationstyle",
|
| 323 |
+
"next": null,
|
| 324 |
+
"parent": null,
|
| 325 |
+
"inputs": {},
|
| 326 |
+
"fields": {
|
| 327 |
+
"STYLE": [
|
| 328 |
+
"left-right",
|
| 329 |
+
null
|
| 330 |
+
]
|
| 331 |
+
},
|
| 332 |
+
"shadow": false,
|
| 333 |
+
"topLevel": true,
|
| 334 |
+
"x": 1128,
|
| 335 |
+
"y": -287
|
| 336 |
+
},
|
| 337 |
+
"motion_xposition": {
|
| 338 |
+
"opcode": "motion_xposition",
|
| 339 |
+
"next": null,
|
| 340 |
+
"parent": null,
|
| 341 |
+
"inputs": {},
|
| 342 |
+
"fields": {},
|
| 343 |
+
"shadow": false,
|
| 344 |
+
"topLevel": true,
|
| 345 |
+
"x": 1193,
|
| 346 |
+
"y": -136
|
| 347 |
+
},
|
| 348 |
+
"motion_yposition": {
|
| 349 |
+
"opcode": "motion_yposition",
|
| 350 |
+
"next": null,
|
| 351 |
+
"parent": null,
|
| 352 |
+
"inputs": {},
|
| 353 |
+
"fields": {},
|
| 354 |
+
"shadow": false,
|
| 355 |
+
"topLevel": true,
|
| 356 |
+
"x": 1181,
|
| 357 |
+
"y": -64
|
| 358 |
+
},
|
| 359 |
+
"motion_direction": {
|
| 360 |
+
"opcode": "motion_direction",
|
| 361 |
+
"next": null,
|
| 362 |
+
"parent": null,
|
| 363 |
+
"inputs": {},
|
| 364 |
+
"fields": {},
|
| 365 |
+
"shadow": false,
|
| 366 |
+
"topLevel": true,
|
| 367 |
+
"x": 1188,
|
| 368 |
+
"y": 21
|
| 369 |
+
}
|
| 370 |
+
}
|
blocks/classwise_blocks/operator_block.json
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"operator_add": {
|
| 3 |
+
"opcode": "operator_add",
|
| 4 |
+
"next": null,
|
| 5 |
+
"parent": null,
|
| 6 |
+
"inputs": {
|
| 7 |
+
"NUM1": [
|
| 8 |
+
1,
|
| 9 |
+
[
|
| 10 |
+
4,
|
| 11 |
+
""
|
| 12 |
+
]
|
| 13 |
+
],
|
| 14 |
+
"NUM2": [
|
| 15 |
+
1,
|
| 16 |
+
[
|
| 17 |
+
4,
|
| 18 |
+
""
|
| 19 |
+
]
|
| 20 |
+
]
|
| 21 |
+
},
|
| 22 |
+
"fields": {},
|
| 23 |
+
"shadow": false,
|
| 24 |
+
"topLevel": true,
|
| 25 |
+
"x": 128,
|
| 26 |
+
"y": 153
|
| 27 |
+
},
|
| 28 |
+
"operator_subtract": {
|
| 29 |
+
"opcode": "operator_subtract",
|
| 30 |
+
"next": null,
|
| 31 |
+
"parent": null,
|
| 32 |
+
"inputs": {
|
| 33 |
+
"NUM1": [
|
| 34 |
+
1,
|
| 35 |
+
[
|
| 36 |
+
4,
|
| 37 |
+
""
|
| 38 |
+
]
|
| 39 |
+
],
|
| 40 |
+
"NUM2": [
|
| 41 |
+
1,
|
| 42 |
+
[
|
| 43 |
+
4,
|
| 44 |
+
""
|
| 45 |
+
]
|
| 46 |
+
]
|
| 47 |
+
},
|
| 48 |
+
"fields": {},
|
| 49 |
+
"shadow": false,
|
| 50 |
+
"topLevel": true,
|
| 51 |
+
"x": 134,
|
| 52 |
+
"y": 214
|
| 53 |
+
},
|
| 54 |
+
"operator_multiply": {
|
| 55 |
+
"opcode": "operator_multiply",
|
| 56 |
+
"next": null,
|
| 57 |
+
"parent": null,
|
| 58 |
+
"inputs": {
|
| 59 |
+
"NUM1": [
|
| 60 |
+
1,
|
| 61 |
+
[
|
| 62 |
+
4,
|
| 63 |
+
""
|
| 64 |
+
]
|
| 65 |
+
],
|
| 66 |
+
"NUM2": [
|
| 67 |
+
1,
|
| 68 |
+
[
|
| 69 |
+
4,
|
| 70 |
+
""
|
| 71 |
+
]
|
| 72 |
+
]
|
| 73 |
+
},
|
| 74 |
+
"fields": {},
|
| 75 |
+
"shadow": false,
|
| 76 |
+
"topLevel": true,
|
| 77 |
+
"x": 134,
|
| 78 |
+
"y": 278
|
| 79 |
+
},
|
| 80 |
+
"operator_divide": {
|
| 81 |
+
"opcode": "operator_divide",
|
| 82 |
+
"next": null,
|
| 83 |
+
"parent": null,
|
| 84 |
+
"inputs": {
|
| 85 |
+
"NUM1": [
|
| 86 |
+
1,
|
| 87 |
+
[
|
| 88 |
+
4,
|
| 89 |
+
""
|
| 90 |
+
]
|
| 91 |
+
],
|
| 92 |
+
"NUM2": [
|
| 93 |
+
1,
|
| 94 |
+
[
|
| 95 |
+
4,
|
| 96 |
+
""
|
| 97 |
+
]
|
| 98 |
+
]
|
| 99 |
+
},
|
| 100 |
+
"fields": {},
|
| 101 |
+
"shadow": false,
|
| 102 |
+
"topLevel": true,
|
| 103 |
+
"x": 138,
|
| 104 |
+
"y": 359
|
| 105 |
+
},
|
| 106 |
+
"operator_random": {
|
| 107 |
+
"opcode": "operator_random",
|
| 108 |
+
"next": null,
|
| 109 |
+
"parent": null,
|
| 110 |
+
"inputs": {
|
| 111 |
+
"FROM": [
|
| 112 |
+
1,
|
| 113 |
+
[
|
| 114 |
+
4,
|
| 115 |
+
"1"
|
| 116 |
+
]
|
| 117 |
+
],
|
| 118 |
+
"TO": [
|
| 119 |
+
1,
|
| 120 |
+
[
|
| 121 |
+
4,
|
| 122 |
+
"10"
|
| 123 |
+
]
|
| 124 |
+
]
|
| 125 |
+
},
|
| 126 |
+
"fields": {},
|
| 127 |
+
"shadow": false,
|
| 128 |
+
"topLevel": true,
|
| 129 |
+
"x": 311,
|
| 130 |
+
"y": 157
|
| 131 |
+
},
|
| 132 |
+
"operator_gt": {
|
| 133 |
+
"opcode": "operator_gt",
|
| 134 |
+
"next": null,
|
| 135 |
+
"parent": null,
|
| 136 |
+
"inputs": {
|
| 137 |
+
"OPERAND1": [
|
| 138 |
+
1,
|
| 139 |
+
[
|
| 140 |
+
10,
|
| 141 |
+
""
|
| 142 |
+
]
|
| 143 |
+
],
|
| 144 |
+
"OPERAND2": [
|
| 145 |
+
1,
|
| 146 |
+
[
|
| 147 |
+
10,
|
| 148 |
+
"50"
|
| 149 |
+
]
|
| 150 |
+
]
|
| 151 |
+
},
|
| 152 |
+
"fields": {},
|
| 153 |
+
"shadow": false,
|
| 154 |
+
"topLevel": true,
|
| 155 |
+
"x": 348,
|
| 156 |
+
"y": 217
|
| 157 |
+
},
|
| 158 |
+
"operator_lt": {
|
| 159 |
+
"opcode": "operator_lt",
|
| 160 |
+
"next": null,
|
| 161 |
+
"parent": null,
|
| 162 |
+
"inputs": {
|
| 163 |
+
"OPERAND1": [
|
| 164 |
+
1,
|
| 165 |
+
[
|
| 166 |
+
10,
|
| 167 |
+
""
|
| 168 |
+
]
|
| 169 |
+
],
|
| 170 |
+
"OPERAND2": [
|
| 171 |
+
1,
|
| 172 |
+
[
|
| 173 |
+
10,
|
| 174 |
+
"50"
|
| 175 |
+
]
|
| 176 |
+
]
|
| 177 |
+
},
|
| 178 |
+
"fields": {},
|
| 179 |
+
"shadow": false,
|
| 180 |
+
"topLevel": true,
|
| 181 |
+
"x": 345,
|
| 182 |
+
"y": 286
|
| 183 |
+
},
|
| 184 |
+
"operator_equals": {
|
| 185 |
+
"opcode": "operator_equals",
|
| 186 |
+
"next": null,
|
| 187 |
+
"parent": null,
|
| 188 |
+
"inputs": {
|
| 189 |
+
"OPERAND1": [
|
| 190 |
+
1,
|
| 191 |
+
[
|
| 192 |
+
10,
|
| 193 |
+
""
|
| 194 |
+
]
|
| 195 |
+
],
|
| 196 |
+
"OPERAND2": [
|
| 197 |
+
1,
|
| 198 |
+
[
|
| 199 |
+
10,
|
| 200 |
+
"50"
|
| 201 |
+
]
|
| 202 |
+
]
|
| 203 |
+
},
|
| 204 |
+
"fields": {},
|
| 205 |
+
"shadow": false,
|
| 206 |
+
"topLevel": true,
|
| 207 |
+
"x": 345,
|
| 208 |
+
"y": 372
|
| 209 |
+
},
|
| 210 |
+
"operator_and": {
|
| 211 |
+
"opcode": "operator_and",
|
| 212 |
+
"next": null,
|
| 213 |
+
"parent": null,
|
| 214 |
+
"inputs": {},
|
| 215 |
+
"fields": {},
|
| 216 |
+
"shadow": false,
|
| 217 |
+
"topLevel": true,
|
| 218 |
+
"x": 701,
|
| 219 |
+
"y": 158
|
| 220 |
+
},
|
| 221 |
+
"operator_or": {
|
| 222 |
+
"opcode": "operator_or",
|
| 223 |
+
"next": null,
|
| 224 |
+
"parent": null,
|
| 225 |
+
"inputs": {},
|
| 226 |
+
"fields": {},
|
| 227 |
+
"shadow": false,
|
| 228 |
+
"topLevel": true,
|
| 229 |
+
"x": 705,
|
| 230 |
+
"y": 222
|
| 231 |
+
},
|
| 232 |
+
"operator_not": {
|
| 233 |
+
"opcode": "operator_not",
|
| 234 |
+
"next": null,
|
| 235 |
+
"parent": null,
|
| 236 |
+
"inputs": {},
|
| 237 |
+
"fields": {},
|
| 238 |
+
"shadow": false,
|
| 239 |
+
"topLevel": true,
|
| 240 |
+
"x": 734,
|
| 241 |
+
"y": 283
|
| 242 |
+
},
|
| 243 |
+
"operator_join": {
|
| 244 |
+
"opcode": "operator_join",
|
| 245 |
+
"next": null,
|
| 246 |
+
"parent": null,
|
| 247 |
+
"inputs": {
|
| 248 |
+
"STRING1": [
|
| 249 |
+
1,
|
| 250 |
+
[
|
| 251 |
+
10,
|
| 252 |
+
"apple "
|
| 253 |
+
]
|
| 254 |
+
],
|
| 255 |
+
"STRING2": [
|
| 256 |
+
1,
|
| 257 |
+
[
|
| 258 |
+
10,
|
| 259 |
+
"banana"
|
| 260 |
+
]
|
| 261 |
+
]
|
| 262 |
+
},
|
| 263 |
+
"fields": {},
|
| 264 |
+
"shadow": false,
|
| 265 |
+
"topLevel": true,
|
| 266 |
+
"x": 663,
|
| 267 |
+
"y": 378
|
| 268 |
+
},
|
| 269 |
+
"operator_letter_of": {
|
| 270 |
+
"opcode": "operator_letter_of",
|
| 271 |
+
"next": null,
|
| 272 |
+
"parent": null,
|
| 273 |
+
"inputs": {
|
| 274 |
+
"LETTER": [
|
| 275 |
+
1,
|
| 276 |
+
[
|
| 277 |
+
6,
|
| 278 |
+
"1"
|
| 279 |
+
]
|
| 280 |
+
],
|
| 281 |
+
"STRING": [
|
| 282 |
+
1,
|
| 283 |
+
[
|
| 284 |
+
10,
|
| 285 |
+
"apple"
|
| 286 |
+
]
|
| 287 |
+
]
|
| 288 |
+
},
|
| 289 |
+
"fields": {},
|
| 290 |
+
"shadow": false,
|
| 291 |
+
"topLevel": true,
|
| 292 |
+
"x": 664,
|
| 293 |
+
"y": 445
|
| 294 |
+
},
|
| 295 |
+
"operator_length": {
|
| 296 |
+
"opcode": "operator_length",
|
| 297 |
+
"next": null,
|
| 298 |
+
"parent": null,
|
| 299 |
+
"inputs": {
|
| 300 |
+
"STRING": [
|
| 301 |
+
1,
|
| 302 |
+
[
|
| 303 |
+
10,
|
| 304 |
+
"apple"
|
| 305 |
+
]
|
| 306 |
+
]
|
| 307 |
+
},
|
| 308 |
+
"fields": {},
|
| 309 |
+
"shadow": false,
|
| 310 |
+
"topLevel": true,
|
| 311 |
+
"x": 664,
|
| 312 |
+
"y": 521
|
| 313 |
+
},
|
| 314 |
+
"operator_contains": {
|
| 315 |
+
"opcode": "operator_contains",
|
| 316 |
+
"next": null,
|
| 317 |
+
"parent": null,
|
| 318 |
+
"inputs": {
|
| 319 |
+
"STRING1": [
|
| 320 |
+
1,
|
| 321 |
+
[
|
| 322 |
+
10,
|
| 323 |
+
"apple"
|
| 324 |
+
]
|
| 325 |
+
],
|
| 326 |
+
"STRING2": [
|
| 327 |
+
1,
|
| 328 |
+
[
|
| 329 |
+
10,
|
| 330 |
+
"a"
|
| 331 |
+
]
|
| 332 |
+
]
|
| 333 |
+
},
|
| 334 |
+
"fields": {},
|
| 335 |
+
"shadow": false,
|
| 336 |
+
"topLevel": true,
|
| 337 |
+
"x": 634,
|
| 338 |
+
"y": 599
|
| 339 |
+
},
|
| 340 |
+
"operator_mod": {
|
| 341 |
+
"opcode": "operator_mod",
|
| 342 |
+
"next": null,
|
| 343 |
+
"parent": null,
|
| 344 |
+
"inputs": {
|
| 345 |
+
"NUM1": [
|
| 346 |
+
1,
|
| 347 |
+
[
|
| 348 |
+
4,
|
| 349 |
+
""
|
| 350 |
+
]
|
| 351 |
+
],
|
| 352 |
+
"NUM2": [
|
| 353 |
+
1,
|
| 354 |
+
[
|
| 355 |
+
4,
|
| 356 |
+
""
|
| 357 |
+
]
|
| 358 |
+
]
|
| 359 |
+
},
|
| 360 |
+
"fields": {},
|
| 361 |
+
"shadow": false,
|
| 362 |
+
"topLevel": true,
|
| 363 |
+
"x": 295,
|
| 364 |
+
"y": 594
|
| 365 |
+
},
|
| 366 |
+
"operator_round": {
|
| 367 |
+
"opcode": "operator_round",
|
| 368 |
+
"next": null,
|
| 369 |
+
"parent": null,
|
| 370 |
+
"inputs": {
|
| 371 |
+
"NUM": [
|
| 372 |
+
1,
|
| 373 |
+
[
|
| 374 |
+
4,
|
| 375 |
+
""
|
| 376 |
+
]
|
| 377 |
+
]
|
| 378 |
+
},
|
| 379 |
+
"fields": {},
|
| 380 |
+
"shadow": false,
|
| 381 |
+
"topLevel": true,
|
| 382 |
+
"x": 307,
|
| 383 |
+
"y": 674
|
| 384 |
+
},
|
| 385 |
+
"operator_mathop": {
|
| 386 |
+
"opcode": "operator_mathop",
|
| 387 |
+
"next": null,
|
| 388 |
+
"parent": null,
|
| 389 |
+
"inputs": {
|
| 390 |
+
"NUM": [
|
| 391 |
+
1,
|
| 392 |
+
[
|
| 393 |
+
4,
|
| 394 |
+
""
|
| 395 |
+
]
|
| 396 |
+
]
|
| 397 |
+
},
|
| 398 |
+
"fields": {
|
| 399 |
+
"OPERATOR": [
|
| 400 |
+
"abs",
|
| 401 |
+
null
|
| 402 |
+
]
|
| 403 |
+
},
|
| 404 |
+
"shadow": false,
|
| 405 |
+
"topLevel": true,
|
| 406 |
+
"x": 280,
|
| 407 |
+
"y": 754
|
| 408 |
+
}
|
| 409 |
+
}
|
blocks/classwise_blocks/sensing_block.json
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"sensing_touchingobject": {
|
| 3 |
+
"opcode": "sensing_touchingobject",
|
| 4 |
+
"next": null,
|
| 5 |
+
"parent": null,
|
| 6 |
+
"inputs": {
|
| 7 |
+
"TOUCHINGOBJECTMENU": [
|
| 8 |
+
1,
|
| 9 |
+
"sensing_touchingobjectmenu"
|
| 10 |
+
]
|
| 11 |
+
},
|
| 12 |
+
"fields": {},
|
| 13 |
+
"shadow": false,
|
| 14 |
+
"topLevel": true,
|
| 15 |
+
"x": 359,
|
| 16 |
+
"y": 116
|
| 17 |
+
},
|
| 18 |
+
"sensing_touchingobjectmenu": {
|
| 19 |
+
"opcode": "sensing_touchingobjectmenu",
|
| 20 |
+
"next": null,
|
| 21 |
+
"parent": "sensing_touchingobject",
|
| 22 |
+
"inputs": {},
|
| 23 |
+
"fields": {
|
| 24 |
+
"TOUCHINGOBJECTMENU": [
|
| 25 |
+
"_mouse_",
|
| 26 |
+
null
|
| 27 |
+
]
|
| 28 |
+
},
|
| 29 |
+
"shadow": true,
|
| 30 |
+
"topLevel": false
|
| 31 |
+
},
|
| 32 |
+
"sensing_touchingcolor": {
|
| 33 |
+
"opcode": "sensing_touchingcolor",
|
| 34 |
+
"next": null,
|
| 35 |
+
"parent": null,
|
| 36 |
+
"inputs": {
|
| 37 |
+
"COLOR": [
|
| 38 |
+
1,
|
| 39 |
+
[
|
| 40 |
+
9,
|
| 41 |
+
"#55b888"
|
| 42 |
+
]
|
| 43 |
+
]
|
| 44 |
+
},
|
| 45 |
+
"fields": {},
|
| 46 |
+
"shadow": false,
|
| 47 |
+
"topLevel": true,
|
| 48 |
+
"x": 360,
|
| 49 |
+
"y": 188
|
| 50 |
+
},
|
| 51 |
+
"sensing_coloristouchingcolor": {
|
| 52 |
+
"opcode": "sensing_coloristouchingcolor",
|
| 53 |
+
"next": null,
|
| 54 |
+
"parent": null,
|
| 55 |
+
"inputs": {
|
| 56 |
+
"COLOR": [
|
| 57 |
+
1,
|
| 58 |
+
[
|
| 59 |
+
9,
|
| 60 |
+
"#d019f2"
|
| 61 |
+
]
|
| 62 |
+
],
|
| 63 |
+
"COLOR2": [
|
| 64 |
+
1,
|
| 65 |
+
[
|
| 66 |
+
9,
|
| 67 |
+
"#2b0de3"
|
| 68 |
+
]
|
| 69 |
+
]
|
| 70 |
+
},
|
| 71 |
+
"fields": {},
|
| 72 |
+
"shadow": false,
|
| 73 |
+
"topLevel": true,
|
| 74 |
+
"x": 348,
|
| 75 |
+
"y": 277
|
| 76 |
+
},
|
| 77 |
+
"sensing_askandwait": {
|
| 78 |
+
"opcode": "sensing_askandwait",
|
| 79 |
+
"next": null,
|
| 80 |
+
"parent": null,
|
| 81 |
+
"inputs": {
|
| 82 |
+
"QUESTION": [
|
| 83 |
+
1,
|
| 84 |
+
[
|
| 85 |
+
10,
|
| 86 |
+
"What's your name?"
|
| 87 |
+
]
|
| 88 |
+
]
|
| 89 |
+
},
|
| 90 |
+
"fields": {},
|
| 91 |
+
"shadow": false,
|
| 92 |
+
"topLevel": true,
|
| 93 |
+
"x": 338,
|
| 94 |
+
"y": 354
|
| 95 |
+
},
|
| 96 |
+
"sensing_answer": {
|
| 97 |
+
"opcode": "sensing_answer",
|
| 98 |
+
"next": null,
|
| 99 |
+
"parent": null,
|
| 100 |
+
"inputs": {},
|
| 101 |
+
"fields": {},
|
| 102 |
+
"shadow": false,
|
| 103 |
+
"topLevel": true,
|
| 104 |
+
"x": 782,
|
| 105 |
+
"y": 111
|
| 106 |
+
},
|
| 107 |
+
"sensing_keypressed": {
|
| 108 |
+
"opcode": "sensing_keypressed",
|
| 109 |
+
"next": null,
|
| 110 |
+
"parent": null,
|
| 111 |
+
"inputs": {
|
| 112 |
+
"KEY_OPTION": [
|
| 113 |
+
1,
|
| 114 |
+
"sensing_keyoptions"
|
| 115 |
+
]
|
| 116 |
+
},
|
| 117 |
+
"fields": {},
|
| 118 |
+
"shadow": false,
|
| 119 |
+
"topLevel": true,
|
| 120 |
+
"x": 762,
|
| 121 |
+
"y": 207
|
| 122 |
+
},
|
| 123 |
+
"sensing_keyoptions": {
|
| 124 |
+
"opcode": "sensing_keyoptions",
|
| 125 |
+
"next": null,
|
| 126 |
+
"parent": "sensing_keypressed",
|
| 127 |
+
"inputs": {},
|
| 128 |
+
"fields": {
|
| 129 |
+
"KEY_OPTION": [
|
| 130 |
+
"space",
|
| 131 |
+
null
|
| 132 |
+
]
|
| 133 |
+
},
|
| 134 |
+
"shadow": true,
|
| 135 |
+
"topLevel": false
|
| 136 |
+
},
|
| 137 |
+
"sensing_mousedown": {
|
| 138 |
+
"opcode": "sensing_mousedown",
|
| 139 |
+
"next": null,
|
| 140 |
+
"parent": null,
|
| 141 |
+
"inputs": {},
|
| 142 |
+
"fields": {},
|
| 143 |
+
"shadow": false,
|
| 144 |
+
"topLevel": true,
|
| 145 |
+
"x": 822,
|
| 146 |
+
"y": 422
|
| 147 |
+
},
|
| 148 |
+
"sensing_mousex": {
|
| 149 |
+
"opcode": "sensing_mousex",
|
| 150 |
+
"next": null,
|
| 151 |
+
"parent": null,
|
| 152 |
+
"inputs": {},
|
| 153 |
+
"fields": {},
|
| 154 |
+
"shadow": false,
|
| 155 |
+
"topLevel": true,
|
| 156 |
+
"x": 302,
|
| 157 |
+
"y": 528
|
| 158 |
+
},
|
| 159 |
+
"sensing_mousey": {
|
| 160 |
+
"opcode": "sensing_mousey",
|
| 161 |
+
"next": null,
|
| 162 |
+
"parent": null,
|
| 163 |
+
"inputs": {},
|
| 164 |
+
"fields": {},
|
| 165 |
+
"shadow": false,
|
| 166 |
+
"topLevel": true,
|
| 167 |
+
"x": 668,
|
| 168 |
+
"y": 547
|
| 169 |
+
},
|
| 170 |
+
"sensing_setdragmode": {
|
| 171 |
+
"opcode": "sensing_setdragmode",
|
| 172 |
+
"next": null,
|
| 173 |
+
"parent": null,
|
| 174 |
+
"inputs": {},
|
| 175 |
+
"fields": {
|
| 176 |
+
"DRAG_MODE": [
|
| 177 |
+
"draggable",
|
| 178 |
+
null
|
| 179 |
+
]
|
| 180 |
+
},
|
| 181 |
+
"shadow": false,
|
| 182 |
+
"topLevel": true,
|
| 183 |
+
"x": 950,
|
| 184 |
+
"y": 574
|
| 185 |
+
},
|
| 186 |
+
"sensing_loudness": {
|
| 187 |
+
"opcode": "sensing_loudness",
|
| 188 |
+
"next": null,
|
| 189 |
+
"parent": null,
|
| 190 |
+
"inputs": {},
|
| 191 |
+
"fields": {},
|
| 192 |
+
"shadow": false,
|
| 193 |
+
"topLevel": true,
|
| 194 |
+
"x": 658,
|
| 195 |
+
"y": 703
|
| 196 |
+
},
|
| 197 |
+
"sensing_timer": {
|
| 198 |
+
"opcode": "sensing_timer",
|
| 199 |
+
"next": null,
|
| 200 |
+
"parent": null,
|
| 201 |
+
"inputs": {},
|
| 202 |
+
"fields": {},
|
| 203 |
+
"shadow": false,
|
| 204 |
+
"topLevel": true,
|
| 205 |
+
"x": 459,
|
| 206 |
+
"y": 671
|
| 207 |
+
},
|
| 208 |
+
"sensing_resettimer": {
|
| 209 |
+
"opcode": "sensing_resettimer",
|
| 210 |
+
"next": null,
|
| 211 |
+
"parent": null,
|
| 212 |
+
"inputs": {},
|
| 213 |
+
"fields": {},
|
| 214 |
+
"shadow": false,
|
| 215 |
+
"topLevel": true,
|
| 216 |
+
"x": 462,
|
| 217 |
+
"y": 781
|
| 218 |
+
},
|
| 219 |
+
"sensing_of": {
|
| 220 |
+
"opcode": "sensing_of",
|
| 221 |
+
"next": null,
|
| 222 |
+
"parent": null,
|
| 223 |
+
"inputs": {
|
| 224 |
+
"OBJECT": [
|
| 225 |
+
1,
|
| 226 |
+
"sensing_of_object_menu"
|
| 227 |
+
]
|
| 228 |
+
},
|
| 229 |
+
"fields": {
|
| 230 |
+
"PROPERTY": [
|
| 231 |
+
"backdrop #",
|
| 232 |
+
null
|
| 233 |
+
]
|
| 234 |
+
},
|
| 235 |
+
"shadow": false,
|
| 236 |
+
"topLevel": true,
|
| 237 |
+
"x": 997,
|
| 238 |
+
"y": 754
|
| 239 |
+
},
|
| 240 |
+
"sensing_of_object_menu": {
|
| 241 |
+
"opcode": "sensing_of_object_menu",
|
| 242 |
+
"next": null,
|
| 243 |
+
"parent": "sensing_of",
|
| 244 |
+
"inputs": {},
|
| 245 |
+
"fields": {
|
| 246 |
+
"OBJECT": [
|
| 247 |
+
"_stage_",
|
| 248 |
+
null
|
| 249 |
+
]
|
| 250 |
+
},
|
| 251 |
+
"shadow": true,
|
| 252 |
+
"topLevel": false
|
| 253 |
+
},
|
| 254 |
+
"sensing_current": {
|
| 255 |
+
"opcode": "sensing_current",
|
| 256 |
+
"next": null,
|
| 257 |
+
"parent": null,
|
| 258 |
+
"inputs": {},
|
| 259 |
+
"fields": {
|
| 260 |
+
"CURRENTMENU": [
|
| 261 |
+
"YEAR",
|
| 262 |
+
null
|
| 263 |
+
]
|
| 264 |
+
},
|
| 265 |
+
"shadow": false,
|
| 266 |
+
"topLevel": true,
|
| 267 |
+
"x": 627,
|
| 268 |
+
"y": 884
|
| 269 |
+
},
|
| 270 |
+
"sensing_dayssince2000": {
|
| 271 |
+
"opcode": "sensing_dayssince2000",
|
| 272 |
+
"next": null,
|
| 273 |
+
"parent": null,
|
| 274 |
+
"inputs": {},
|
| 275 |
+
"fields": {},
|
| 276 |
+
"shadow": false,
|
| 277 |
+
"topLevel": true,
|
| 278 |
+
"x": 959,
|
| 279 |
+
"y": 903
|
| 280 |
+
},
|
| 281 |
+
"sensing_username": {
|
| 282 |
+
"opcode": "sensing_username",
|
| 283 |
+
"next": null,
|
| 284 |
+
"parent": null,
|
| 285 |
+
"inputs": {},
|
| 286 |
+
"fields": {},
|
| 287 |
+
"shadow": false,
|
| 288 |
+
"topLevel": true,
|
| 289 |
+
"x": 833,
|
| 290 |
+
"y": 757
|
| 291 |
+
}
|
| 292 |
+
}
|
blocks/classwise_blocks/sound_block.json
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"sound_playuntildone": {
|
| 3 |
+
"opcode": "sound_playuntildone",
|
| 4 |
+
"next": null,
|
| 5 |
+
"parent": null,
|
| 6 |
+
"inputs": {
|
| 7 |
+
"SOUND_MENU": [
|
| 8 |
+
1,
|
| 9 |
+
"sound_sounds_menu"
|
| 10 |
+
]
|
| 11 |
+
},
|
| 12 |
+
"fields": {},
|
| 13 |
+
"shadow": false,
|
| 14 |
+
"topLevel": true,
|
| 15 |
+
"x": 253,
|
| 16 |
+
"y": 17
|
| 17 |
+
},
|
| 18 |
+
"sound_sounds_menu": {
|
| 19 |
+
"opcode": "sound_sounds_menu",
|
| 20 |
+
"next": null,
|
| 21 |
+
"parent": "sound_playuntildone and sound_play",
|
| 22 |
+
"inputs": {},
|
| 23 |
+
"fields": {
|
| 24 |
+
"SOUND_MENU": [
|
| 25 |
+
"Meow",
|
| 26 |
+
null
|
| 27 |
+
]
|
| 28 |
+
},
|
| 29 |
+
"shadow": true,
|
| 30 |
+
"topLevel": false
|
| 31 |
+
},
|
| 32 |
+
"sound_play": {
|
| 33 |
+
"opcode": "sound_play",
|
| 34 |
+
"next": null,
|
| 35 |
+
"parent": null,
|
| 36 |
+
"inputs": {
|
| 37 |
+
"SOUND_MENU": [
|
| 38 |
+
1,
|
| 39 |
+
"sound_sounds_menu"
|
| 40 |
+
]
|
| 41 |
+
},
|
| 42 |
+
"fields": {},
|
| 43 |
+
"shadow": false,
|
| 44 |
+
"topLevel": true,
|
| 45 |
+
"x": 245,
|
| 46 |
+
"y": 122
|
| 47 |
+
},
|
| 48 |
+
"sound_stopallsounds": {
|
| 49 |
+
"opcode": "sound_stopallsounds",
|
| 50 |
+
"next": null,
|
| 51 |
+
"parent": null,
|
| 52 |
+
"inputs": {},
|
| 53 |
+
"fields": {},
|
| 54 |
+
"shadow": false,
|
| 55 |
+
"topLevel": true,
|
| 56 |
+
"x": 253,
|
| 57 |
+
"y": 245
|
| 58 |
+
},
|
| 59 |
+
"sound_changeeffectby": {
|
| 60 |
+
"opcode": "sound_changeeffectby",
|
| 61 |
+
"next": null,
|
| 62 |
+
"parent": null,
|
| 63 |
+
"inputs": {
|
| 64 |
+
"VALUE": [
|
| 65 |
+
1,
|
| 66 |
+
[
|
| 67 |
+
4,
|
| 68 |
+
"10"
|
| 69 |
+
]
|
| 70 |
+
]
|
| 71 |
+
},
|
| 72 |
+
"fields": {
|
| 73 |
+
"EFFECT": [
|
| 74 |
+
"PITCH",
|
| 75 |
+
null
|
| 76 |
+
]
|
| 77 |
+
},
|
| 78 |
+
"shadow": false,
|
| 79 |
+
"topLevel": true,
|
| 80 |
+
"x": 653,
|
| 81 |
+
"y": 14
|
| 82 |
+
},
|
| 83 |
+
"sound_seteffectto": {
|
| 84 |
+
"opcode": "sound_seteffectto",
|
| 85 |
+
"next": null,
|
| 86 |
+
"parent": null,
|
| 87 |
+
"inputs": {
|
| 88 |
+
"VALUE": [
|
| 89 |
+
1,
|
| 90 |
+
[
|
| 91 |
+
4,
|
| 92 |
+
"100"
|
| 93 |
+
]
|
| 94 |
+
]
|
| 95 |
+
},
|
| 96 |
+
"fields": {
|
| 97 |
+
"EFFECT": [
|
| 98 |
+
"PITCH",
|
| 99 |
+
null
|
| 100 |
+
]
|
| 101 |
+
},
|
| 102 |
+
"shadow": false,
|
| 103 |
+
"topLevel": true,
|
| 104 |
+
"x": 653,
|
| 105 |
+
"y": 139
|
| 106 |
+
},
|
| 107 |
+
"sound_cleareffects": {
|
| 108 |
+
"opcode": "sound_cleareffects",
|
| 109 |
+
"next": null,
|
| 110 |
+
"parent": null,
|
| 111 |
+
"inputs": {},
|
| 112 |
+
"fields": {},
|
| 113 |
+
"shadow": false,
|
| 114 |
+
"topLevel": true,
|
| 115 |
+
"x": 651,
|
| 116 |
+
"y": 242
|
| 117 |
+
},
|
| 118 |
+
"sound_changevolumeby": {
|
| 119 |
+
"opcode": "sound_changevolumeby",
|
| 120 |
+
"next": null,
|
| 121 |
+
"parent": null,
|
| 122 |
+
"inputs": {
|
| 123 |
+
"VOLUME": [
|
| 124 |
+
1,
|
| 125 |
+
[
|
| 126 |
+
4,
|
| 127 |
+
"-10"
|
| 128 |
+
]
|
| 129 |
+
]
|
| 130 |
+
},
|
| 131 |
+
"fields": {},
|
| 132 |
+
"shadow": false,
|
| 133 |
+
"topLevel": true,
|
| 134 |
+
"x": 645,
|
| 135 |
+
"y": 353
|
| 136 |
+
},
|
| 137 |
+
"sound_setvolumeto": {
|
| 138 |
+
"opcode": "sound_setvolumeto",
|
| 139 |
+
"next": null,
|
| 140 |
+
"parent": null,
|
| 141 |
+
"inputs": {
|
| 142 |
+
"VOLUME": [
|
| 143 |
+
1,
|
| 144 |
+
[
|
| 145 |
+
4,
|
| 146 |
+
"100"
|
| 147 |
+
]
|
| 148 |
+
]
|
| 149 |
+
},
|
| 150 |
+
"fields": {},
|
| 151 |
+
"shadow": false,
|
| 152 |
+
"topLevel": true,
|
| 153 |
+
"x": 1108,
|
| 154 |
+
"y": 5
|
| 155 |
+
},
|
| 156 |
+
"sound_volume": {
|
| 157 |
+
"opcode": "sound_volume",
|
| 158 |
+
"next": null,
|
| 159 |
+
"parent": null,
|
| 160 |
+
"inputs": {},
|
| 161 |
+
"fields": {},
|
| 162 |
+
"shadow": false,
|
| 163 |
+
"topLevel": true,
|
| 164 |
+
"x": 1136,
|
| 165 |
+
"y": 123
|
| 166 |
+
}
|
| 167 |
+
}
|
blocks/hat_blocks.json
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"block_category": "Hat Blocks",
|
| 3 |
+
"description": "Hat blocks are characterized by a rounded top and a bump at the bottom. They initiate scripts, meaning they are the starting point for a sequence of interconnected blocks.",
|
| 4 |
+
"blocks": [
|
| 5 |
+
{
|
| 6 |
+
"block_name": "when green flag pressed",
|
| 7 |
+
"block_type": "Events",
|
| 8 |
+
"op_code": "event_whenflagclicked",
|
| 9 |
+
"block_shape": "Hat Block",
|
| 10 |
+
"functionality": "This Hat block initiates the script when the green flag is clicked, serving as the common starting point for most Scratch projects.",
|
| 11 |
+
"inputs": null,
|
| 12 |
+
"example_standalone": "when green flag clicked",
|
| 13 |
+
"example_with_other_blocks": [
|
| 14 |
+
{
|
| 15 |
+
"script": "when green flag clicked\n go to x: (0) y: (0)\n say [Hello!] for (2) seconds\nend",
|
| 16 |
+
"explanation": "This script makes the sprite go to the center of the stage and then say 'Hello!' for 2 seconds when the green flag is clicked."
|
| 17 |
+
}
|
| 18 |
+
]
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
"block_name": "when () key pressed",
|
| 22 |
+
"block_type": "Events",
|
| 23 |
+
"op_code": "event_whenkeypressed",
|
| 24 |
+
"block_shape": "Hat Block",
|
| 25 |
+
"functionality": "This Hat block initiates the script when a specified keyboard key is pressed.",
|
| 26 |
+
"inputs": [
|
| 27 |
+
{
|
| 28 |
+
"name": "key",
|
| 29 |
+
"type": "dropdown",
|
| 30 |
+
"options": [
|
| 31 |
+
"space",
|
| 32 |
+
"up arrow",
|
| 33 |
+
"down arrow",
|
| 34 |
+
"right arrow",
|
| 35 |
+
"left arrow",
|
| 36 |
+
"any",
|
| 37 |
+
"a",
|
| 38 |
+
"b",
|
| 39 |
+
"c",
|
| 40 |
+
"d",
|
| 41 |
+
"e",
|
| 42 |
+
"f",
|
| 43 |
+
"g",
|
| 44 |
+
"h",
|
| 45 |
+
"i",
|
| 46 |
+
"j",
|
| 47 |
+
"k",
|
| 48 |
+
"l",
|
| 49 |
+
"m",
|
| 50 |
+
"n",
|
| 51 |
+
"o",
|
| 52 |
+
"p",
|
| 53 |
+
"q",
|
| 54 |
+
"r",
|
| 55 |
+
"s",
|
| 56 |
+
"t",
|
| 57 |
+
"u",
|
| 58 |
+
"v",
|
| 59 |
+
"w",
|
| 60 |
+
"x",
|
| 61 |
+
"y",
|
| 62 |
+
"z",
|
| 63 |
+
"0",
|
| 64 |
+
"1",
|
| 65 |
+
"2",
|
| 66 |
+
"3",
|
| 67 |
+
"4",
|
| 68 |
+
"5",
|
| 69 |
+
"6",
|
| 70 |
+
"7",
|
| 71 |
+
"8",
|
| 72 |
+
"9"
|
| 73 |
+
]
|
| 74 |
+
}
|
| 75 |
+
],
|
| 76 |
+
"example_standalone": "when [space v] key pressed",
|
| 77 |
+
"example_with_other_blocks": [
|
| 78 |
+
{
|
| 79 |
+
"script": "when [space v] key pressed\n repeat (10)\n change y by (10)\n wait (0.1) seconds\n change y by (-10)\n end",
|
| 80 |
+
"explanation": "This script makes the sprite jump when the spacebar is pressed."
|
| 81 |
+
},
|
| 82 |
+
{
|
| 83 |
+
"script": "when [right arrow v] key pressed\n point in direction (90)\n move (10) steps\nend",
|
| 84 |
+
"explanation": "This script moves the sprite right when the right arrow key is pressed."
|
| 85 |
+
}
|
| 86 |
+
]
|
| 87 |
+
},
|
| 88 |
+
{
|
| 89 |
+
"block_name": "when this sprite clicked",
|
| 90 |
+
"block_type": "Events",
|
| 91 |
+
"op_code": "event_whenthisspriteclicked",
|
| 92 |
+
"block_shape": "Hat Block",
|
| 93 |
+
"functionality": "This Hat block starts the script when the sprite itself is clicked.",
|
| 94 |
+
"inputs": null,
|
| 95 |
+
"example_standalone": "when this sprite clicked",
|
| 96 |
+
"example_with_other_blocks": [
|
| 97 |
+
{
|
| 98 |
+
"script": "when this sprite clicked\n say [Ouch!] for (1) seconds\n change [score v] by (-1)\nend",
|
| 99 |
+
"explanation": "This script makes the sprite say 'Ouch!' and decreases the score by 1 when the sprite is clicked."
|
| 100 |
+
}
|
| 101 |
+
]
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"block_name": "when backdrop switches to ()",
|
| 105 |
+
"block_type": "Events",
|
| 106 |
+
"op_code": "event_whenbackdropswitchesto",
|
| 107 |
+
"block_shape": "Hat Block",
|
| 108 |
+
"functionality": "This Hat block triggers the script when the stage backdrop changes to a specified backdrop.",
|
| 109 |
+
"inputs": [
|
| 110 |
+
{
|
| 111 |
+
"name": "backdrop name",
|
| 112 |
+
"type": "dropdown",
|
| 113 |
+
"options": ["backdrop1", "backdrop2", "..."]
|
| 114 |
+
}
|
| 115 |
+
],
|
| 116 |
+
"example_standalone": "when backdrop switches to [game over v]",
|
| 117 |
+
"example_with_other_blocks": [
|
| 118 |
+
{
|
| 119 |
+
"script": "when backdrop switches to [game over v]\n stop [all v]\nend",
|
| 120 |
+
"explanation": "This script stops all running processes when the backdrop changes to 'game over'."
|
| 121 |
+
},
|
| 122 |
+
{
|
| 123 |
+
"script": "when backdrop switches to [level completed v]\n stop [all v]\nend",
|
| 124 |
+
"explanation": "This script stops all running processes when the backdrop changes to 'level completed'."
|
| 125 |
+
}
|
| 126 |
+
]
|
| 127 |
+
},
|
| 128 |
+
{
|
| 129 |
+
"block_name": "when () > ()",
|
| 130 |
+
"block_type": "Events",
|
| 131 |
+
"op_code": "event_whengreaterthan",
|
| 132 |
+
"block_shape": "Hat Block",
|
| 133 |
+
"functionality": "This Hat block starts the script when a certain value (e.g., loudness from a microphone, or the timer) exceeds a defined threshold.",
|
| 134 |
+
"inputs": [
|
| 135 |
+
{
|
| 136 |
+
"name": "value type",
|
| 137 |
+
"type": "dropdown",
|
| 138 |
+
"options": [
|
| 139 |
+
"loudness",
|
| 140 |
+
"timer"
|
| 141 |
+
]
|
| 142 |
+
},
|
| 143 |
+
{
|
| 144 |
+
"name": "threshold",
|
| 145 |
+
"type": "number"
|
| 146 |
+
}
|
| 147 |
+
],
|
| 148 |
+
"example_standalone": "when [loudness v] > (70)",
|
| 149 |
+
"example_with_other_blocks": [
|
| 150 |
+
{
|
| 151 |
+
"script": "when [loudness v] > (70)\n start sound [scream v]\nend",
|
| 152 |
+
"explanation": "This script starts a 'scream' sound when the microphone loudness exceeds 70."
|
| 153 |
+
}
|
| 154 |
+
]
|
| 155 |
+
},
|
| 156 |
+
{
|
| 157 |
+
"block_name": "when I receive ()",
|
| 158 |
+
"block_type": "Events",
|
| 159 |
+
"op_code": "event_whenbroadcastreceived",
|
| 160 |
+
"block_shape": "Hat Block",
|
| 161 |
+
"functionality": "This Hat block initiates the script upon the reception of a specific broadcast message. This mechanism facilitates indirect communication between sprites or the stage.",
|
| 162 |
+
"inputs": [
|
| 163 |
+
{
|
| 164 |
+
"name": "message name",
|
| 165 |
+
"type": "dropdown",
|
| 166 |
+
"options": ["message1", "message2", "new message..."]
|
| 167 |
+
}
|
| 168 |
+
],
|
| 169 |
+
"example_standalone": "when I receive [start game v]",
|
| 170 |
+
"example_with_other_blocks": [
|
| 171 |
+
{
|
| 172 |
+
"script": "when I receive [start game v]\n show\n go to x: (0) y: (0)\nend",
|
| 173 |
+
"explanation": "This script makes the sprite visible and moves it to the center of the stage when it receives the 'start game' broadcast."
|
| 174 |
+
},
|
| 175 |
+
{
|
| 176 |
+
"script": "when I receive [game over v]\n set score to 0\n stop [all v]\nend",
|
| 177 |
+
"explanation": "This script stops all and resets the score on stage when it receives the 'game over' broadcast."
|
| 178 |
+
}
|
| 179 |
+
]
|
| 180 |
+
},
|
| 181 |
+
{
|
| 182 |
+
"block_name": "When I Start as a Clone",
|
| 183 |
+
"block_type": "Control",
|
| 184 |
+
"op_code": "control_start_as_clone",
|
| 185 |
+
"block_shape": "Hat Block",
|
| 186 |
+
"functionality": "This Hat block initiates the script when a clone of the sprite is created. It defines the behavior of individual clones.",
|
| 187 |
+
"inputs": null,
|
| 188 |
+
"example_standalone": "When I Start as a Clone",
|
| 189 |
+
"example_with_other_blocks": [
|
| 190 |
+
{
|
| 191 |
+
"script": "when I start as a clone\n go to x: (pick random -240 to 240) y: (pick random -180 to 180)\n show\n forever\n move (10) steps\n if on edge, bounce\n end\nend",
|
| 192 |
+
"explanation": "This script makes a newly created clone appear at a random position, become visible, and then continuously move 10 steps, bouncing if it hits an edge."
|
| 193 |
+
}
|
| 194 |
+
]
|
| 195 |
+
},
|
| 196 |
+
{
|
| 197 |
+
"block_name": "define [my custom block]",
|
| 198 |
+
"block_type": "My Blocks",
|
| 199 |
+
"op_code": "procedures_definition",
|
| 200 |
+
"block_shape": "Hat Block",
|
| 201 |
+
"functionality": "This Hat block serves as the definition header for a custom block's script. It allows users to define reusable sequences of code by specifying the block's name and any input parameters it will accept. This promotes modularity and abstraction in projects.",
|
| 202 |
+
"inputs": [
|
| 203 |
+
{
|
| 204 |
+
"name": "PROCCONTAINER",
|
| 205 |
+
"type": "block_prototype"
|
| 206 |
+
}
|
| 207 |
+
],
|
| 208 |
+
"example_standalone": "define jump (height)",
|
| 209 |
+
"example_with_other_blocks": [
|
| 210 |
+
{
|
| 211 |
+
"script": "define jump (height)\n change y by (height)\n wait (0.5) seconds\n change y by (0 - (height))\nend\n\nwhen green flag clicked\n jump (50)\nend",
|
| 212 |
+
"explanation": "This script first defines a custom block named 'jump' that takes a numerical input 'height'. The definition outlines the actions for jumping up and then down. Later, 'jump (50)' is called to make the sprite jump 50 units."
|
| 213 |
+
}
|
| 214 |
+
]
|
| 215 |
+
}
|
| 216 |
+
]
|
| 217 |
+
}
|
blocks/reporter_blocks.json
ADDED
|
@@ -0,0 +1,709 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"block_category": "Reporter Blocks",
|
| 3 |
+
"description": "Reporter blocks have rounded edges. Their purpose is to report values, which can be numbers or strings, and are designed to fit into input slots of other blocks.",
|
| 4 |
+
"blocks": [
|
| 5 |
+
{
|
| 6 |
+
"block_name": "(x position)",
|
| 7 |
+
"block_type": "Motion",
|
| 8 |
+
"op_code": "motion_xposition",
|
| 9 |
+
"block_shape": "Reporter Block",
|
| 10 |
+
"functionality": "Reports the current X-coordinate of the sprite.[NOTE: not used in stage/backdrops]",
|
| 11 |
+
"inputs": null,
|
| 12 |
+
"example_standalone": "x position",
|
| 13 |
+
"example_with_other_blocks": [
|
| 14 |
+
{
|
| 15 |
+
"script": "when green flag clicked\n say (x position) for (2) seconds\nend",
|
| 16 |
+
"explanation": "This script makes the sprite say its current X-coordinate for 2 seconds."
|
| 17 |
+
}
|
| 18 |
+
]
|
| 19 |
+
},
|
| 20 |
+
{
|
| 21 |
+
"block_name": "(y position)",
|
| 22 |
+
"block_type": "Motion",
|
| 23 |
+
"op_code": "motion_yposition",
|
| 24 |
+
"block_shape": "Reporter Block",
|
| 25 |
+
"functionality": "Reports the current Y coordinate of the sprite on the stage.[NOTE: not used in stage/backdrops]",
|
| 26 |
+
"inputs": null,
|
| 27 |
+
"example_standalone": "y position",
|
| 28 |
+
"example_with_other_blocks": [
|
| 29 |
+
{
|
| 30 |
+
"script": "set [worms v] to (y position)",
|
| 31 |
+
"explanation": "This script assigns the sprite's current Y position to the 'worms' variable."
|
| 32 |
+
}
|
| 33 |
+
]
|
| 34 |
+
},
|
| 35 |
+
{
|
| 36 |
+
"block_name": "(direction)",
|
| 37 |
+
"block_type": "Motion",
|
| 38 |
+
"op_code": "motion_direction",
|
| 39 |
+
"block_shape": "Reporter Block",
|
| 40 |
+
"functionality": "Reports the current direction of the sprite in degrees (0 = up, 90 = right, 180 = down, -90 = left).[NOTE: not used in stage/backdrops]",
|
| 41 |
+
"inputs": null,
|
| 42 |
+
"example_standalone": "direction",
|
| 43 |
+
"example_with_other_blocks": [
|
| 44 |
+
{
|
| 45 |
+
"script": "when green flag clicked\n say (direction) for (2) seconds\nend",
|
| 46 |
+
"explanation": "This script makes the sprite say its current direction in degrees for 2 seconds."
|
| 47 |
+
}
|
| 48 |
+
]
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"block_name": "(costume ())",
|
| 52 |
+
"block_type": "Looks",
|
| 53 |
+
"op_code": "looks_costumenumbername",
|
| 54 |
+
"block_shape": "Reporter Block",
|
| 55 |
+
"functionality": "Reports the current costume's number or name.",
|
| 56 |
+
"inputs": [
|
| 57 |
+
{
|
| 58 |
+
"name": "NUMBER_NAME",
|
| 59 |
+
"type": "dropdown",
|
| 60 |
+
"options": [
|
| 61 |
+
"number",
|
| 62 |
+
"name"
|
| 63 |
+
]
|
| 64 |
+
}
|
| 65 |
+
],
|
| 66 |
+
"example_standalone": "costume [number v]",
|
| 67 |
+
"example_with_other_blocks": [
|
| 68 |
+
{
|
| 69 |
+
"script": "say join [I am costume ] (costume [name v])",
|
| 70 |
+
"explanation": "This script makes the sprite display its current costume name in a speech bubble."
|
| 71 |
+
}
|
| 72 |
+
]
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"block_name": "(size)",
|
| 76 |
+
"block_type": "Looks",
|
| 77 |
+
"op_code": "looks_size",
|
| 78 |
+
"block_shape": "Reporter Block",
|
| 79 |
+
"functionality": "Reports the current size of the sprite as a percentage.",
|
| 80 |
+
"inputs": null,
|
| 81 |
+
"example_standalone": "size",
|
| 82 |
+
"example_with_other_blocks": [
|
| 83 |
+
{
|
| 84 |
+
"script": "set size to ( (size) + (10) )",
|
| 85 |
+
"explanation": "This script increases the sprite's size by 10% from its current size."
|
| 86 |
+
}
|
| 87 |
+
]
|
| 88 |
+
},
|
| 89 |
+
{
|
| 90 |
+
"block_name": "(backdrop ())",
|
| 91 |
+
"block_type": "Looks",
|
| 92 |
+
"op_code": "looks_backdropnumbername",
|
| 93 |
+
"block_shape": "Reporter Block",
|
| 94 |
+
"functionality": "Reports the current backdrop's number or name.",
|
| 95 |
+
"inputs": [
|
| 96 |
+
{
|
| 97 |
+
"name": "NUMBER_NAME",
|
| 98 |
+
"type": "dropdown",
|
| 99 |
+
"options": [
|
| 100 |
+
"number",
|
| 101 |
+
"name"
|
| 102 |
+
]
|
| 103 |
+
}
|
| 104 |
+
],
|
| 105 |
+
"example_standalone": "(backdrop [number v])",
|
| 106 |
+
"example_with_other_blocks": [
|
| 107 |
+
{
|
| 108 |
+
"script": "say join [Current backdrop: ] (backdrop [name v]) for (2) seconds",
|
| 109 |
+
"explanation": "This script makes the sprite say the name of the current stage backdrop for 2 seconds."
|
| 110 |
+
}
|
| 111 |
+
]
|
| 112 |
+
},
|
| 113 |
+
{
|
| 114 |
+
"block_name": "(volume)",
|
| 115 |
+
"block_type": "Sound",
|
| 116 |
+
"op_code": "sound_volume",
|
| 117 |
+
"block_shape": "Reporter Block",
|
| 118 |
+
"functionality": "Reports the current volume level of the sprite.",
|
| 119 |
+
"inputs": null,
|
| 120 |
+
"example_standalone": "volume",
|
| 121 |
+
"example_with_other_blocks": [
|
| 122 |
+
{
|
| 123 |
+
"script": "say join [Current volume: ] (volume)",
|
| 124 |
+
"explanation": "This script makes the sprite display its current volume level in a speech bubble."
|
| 125 |
+
}
|
| 126 |
+
]
|
| 127 |
+
},
|
| 128 |
+
{
|
| 129 |
+
"block_name": "(distance to ())",
|
| 130 |
+
"block_type": "Sensing",
|
| 131 |
+
"op_code": "sensing_distanceto",
|
| 132 |
+
"block_shape": "Reporter Block",
|
| 133 |
+
"functionality": "Reports the distance from the current sprite to the mouse-pointer or another specified sprite.",
|
| 134 |
+
"inputs": [
|
| 135 |
+
{
|
| 136 |
+
"name": "target",
|
| 137 |
+
"type": "dropdown",
|
| 138 |
+
"options": ["mouse-pointer", "Sprite1", "Sprite2", "...", "_edge_"]
|
| 139 |
+
}
|
| 140 |
+
],
|
| 141 |
+
"example_standalone": "distance to [mouse-pointer v]",
|
| 142 |
+
"example_with_other_blocks": [
|
| 143 |
+
{
|
| 144 |
+
"script": "if <(distance to [Sprite2 v]) < (50)> then\n say [Too close!]\nend",
|
| 145 |
+
"explanation": "This script makes the sprite say 'Too close!' if it is less than 50 steps away from 'Sprite2'."
|
| 146 |
+
}
|
| 147 |
+
]
|
| 148 |
+
},
|
| 149 |
+
{
|
| 150 |
+
"block_name": "(answer)",
|
| 151 |
+
"block_type": "Sensing",
|
| 152 |
+
"op_code": "sensing_answer",
|
| 153 |
+
"block_shape": "Reporter Block",
|
| 154 |
+
"functionality": "Holds the most recent text inputted using the 'Ask () and Wait' block.",
|
| 155 |
+
"inputs": null,
|
| 156 |
+
"example_standalone": "answer",
|
| 157 |
+
"example_with_other_blocks": [
|
| 158 |
+
{
|
| 159 |
+
"script": "ask [What is your name?] and wait\n say join [Hello ] (answer)",
|
| 160 |
+
"explanation": "This script prompts the user for their name and then uses the 'answer' block to incorporate their input into a greeting."
|
| 161 |
+
}
|
| 162 |
+
]
|
| 163 |
+
},
|
| 164 |
+
{
|
| 165 |
+
"block_name": "(mouse x)",
|
| 166 |
+
"block_type": "Sensing",
|
| 167 |
+
"op_code": "sensing_mousex",
|
| 168 |
+
"block_shape": "Reporter Block",
|
| 169 |
+
"functionality": "Reports the mouse-pointer’s current X position on the stage.",
|
| 170 |
+
"inputs": null,
|
| 171 |
+
"example_standalone": "mouse x",
|
| 172 |
+
"example_with_other_blocks": [
|
| 173 |
+
{
|
| 174 |
+
"script": "go to x: (mouse x) y: (mouse y)",
|
| 175 |
+
"explanation": "This script makes the sprite follow the mouse pointer's X and Y coordinates."
|
| 176 |
+
}
|
| 177 |
+
]
|
| 178 |
+
},
|
| 179 |
+
{
|
| 180 |
+
"block_name": "(mouse y)",
|
| 181 |
+
"block_type": "Sensing",
|
| 182 |
+
"op_code": "sensing_mousey",
|
| 183 |
+
"block_shape": "Reporter Block",
|
| 184 |
+
"functionality": "Reports the mouse-pointer’s current Y position on the stage.",
|
| 185 |
+
"inputs": null,
|
| 186 |
+
"example_standalone": "mouse y",
|
| 187 |
+
"example_with_other_blocks": [
|
| 188 |
+
{
|
| 189 |
+
"script": "if <(mouse y) < (0)> then\n say [Below center]",
|
| 190 |
+
"explanation": "This script makes the sprite say 'Below center' if the mouse pointer is in the lower half of the stage."
|
| 191 |
+
}
|
| 192 |
+
]
|
| 193 |
+
},
|
| 194 |
+
{
|
| 195 |
+
"block_name": "(loudness)",
|
| 196 |
+
"block_type": "Sensing",
|
| 197 |
+
"op_code": "sensing_loudness",
|
| 198 |
+
"block_shape": "Reporter Block",
|
| 199 |
+
"functionality": "Reports the loudness of noise received by a microphone on a scale of 0 to 100.",
|
| 200 |
+
"inputs": null,
|
| 201 |
+
"example_standalone": "loudness",
|
| 202 |
+
"example_with_other_blocks": [
|
| 203 |
+
{
|
| 204 |
+
"script": "when green flag clicked\n forever\n if <(loudness) > (30)> then\n start sound [pop v]\nend",
|
| 205 |
+
"explanation": "This script continuously checks the microphone loudness and plays a 'pop' sound if it exceeds 30."
|
| 206 |
+
}
|
| 207 |
+
]
|
| 208 |
+
},
|
| 209 |
+
{
|
| 210 |
+
"block_name": "(timer)",
|
| 211 |
+
"block_type": "Sensing",
|
| 212 |
+
"op_code": "sensing_timer",
|
| 213 |
+
"block_shape": "Reporter Block",
|
| 214 |
+
"functionality": "Reports the elapsed time since Scratch was launched or the timer was reset, increasing by 1 every second.",
|
| 215 |
+
"inputs": null,
|
| 216 |
+
"example_standalone": "timer",
|
| 217 |
+
"example_with_other_blocks": [
|
| 218 |
+
{
|
| 219 |
+
"script": "when green flag clicked\n reset timer\n wait (5) seconds\n say join [Time elapsed: ] (timer)",
|
| 220 |
+
"explanation": "This script resets the timer when the green flag is clicked, waits for 5 seconds, and then reports the elapsed time."
|
| 221 |
+
}
|
| 222 |
+
]
|
| 223 |
+
},
|
| 224 |
+
{
|
| 225 |
+
"block_name": "(() of ())",
|
| 226 |
+
"block_type": "Sensing",
|
| 227 |
+
"op_code": "sensing_of",
|
| 228 |
+
"block_shape": "Reporter Block",
|
| 229 |
+
"functionality": "Reports a specified value (e.g., x position, direction, costume number) of a specified sprite or the Stage to be accessed in current sprite or stage.",
|
| 230 |
+
"inputs": [
|
| 231 |
+
{
|
| 232 |
+
"name": "value to report",
|
| 233 |
+
"type": "dropdown",
|
| 234 |
+
"options": [
|
| 235 |
+
"x position",
|
| 236 |
+
"y position",
|
| 237 |
+
"direction",
|
| 238 |
+
"costume #",
|
| 239 |
+
"costume name",
|
| 240 |
+
"size",
|
| 241 |
+
"volume",
|
| 242 |
+
"backdrop #",
|
| 243 |
+
"backdrop name"
|
| 244 |
+
]
|
| 245 |
+
},
|
| 246 |
+
{
|
| 247 |
+
"name": "sprite/stage",
|
| 248 |
+
"type": "dropdown",
|
| 249 |
+
"options": ["Stage", "Sprite1", "Sprite2", "...", "_edge_"]
|
| 250 |
+
}
|
| 251 |
+
],
|
| 252 |
+
"example_standalone": "x position of [Sprite1 v]",
|
| 253 |
+
"example_with_other_blocks": [
|
| 254 |
+
{
|
| 255 |
+
"script": "set [other sprite X v] to ( (x position) of [Sprite2 v] )",
|
| 256 |
+
"explanation": "This script sets the 'other sprite X' variable to the current X-position of 'Sprite2'."
|
| 257 |
+
}
|
| 258 |
+
]
|
| 259 |
+
},
|
| 260 |
+
{
|
| 261 |
+
"block_name": "(current ())",
|
| 262 |
+
"block_type": "Sensing",
|
| 263 |
+
"op_code": "sensing_current",
|
| 264 |
+
"block_shape": "Reporter Block",
|
| 265 |
+
"functionality": "Reports the current local year, month, date, day of the week, hour, minutes, or seconds.",
|
| 266 |
+
"inputs": [
|
| 267 |
+
{
|
| 268 |
+
"name": "time unit",
|
| 269 |
+
"type": "dropdown",
|
| 270 |
+
"options": [
|
| 271 |
+
"year",
|
| 272 |
+
"month",
|
| 273 |
+
"date",
|
| 274 |
+
"day of week",
|
| 275 |
+
"hour",
|
| 276 |
+
"minute",
|
| 277 |
+
"second"
|
| 278 |
+
]
|
| 279 |
+
}
|
| 280 |
+
],
|
| 281 |
+
"example_standalone": "current [hour v]",
|
| 282 |
+
"example_with_other_blocks": [
|
| 283 |
+
{
|
| 284 |
+
"script": "say join [The current hour is ] (current [hour v])",
|
| 285 |
+
"explanation": "This script makes the sprite say the current hour."
|
| 286 |
+
}
|
| 287 |
+
]
|
| 288 |
+
},
|
| 289 |
+
{
|
| 290 |
+
"block_name": "(days since 2000)",
|
| 291 |
+
"block_type": "Sensing",
|
| 292 |
+
"op_code": "sensing_dayssince2000",
|
| 293 |
+
"block_shape": "Reporter Block",
|
| 294 |
+
"functionality": "Reports the number of days (and fractions of a day) since 00:00:00 UTC on January 1, 2000.",
|
| 295 |
+
"inputs": null,
|
| 296 |
+
"example_standalone": "days since 2000",
|
| 297 |
+
"example_with_other_blocks": [
|
| 298 |
+
{
|
| 299 |
+
"script": "say join [Days passed: ] (days since 2000)",
|
| 300 |
+
"explanation": "This script makes the sprite display the number of days that have passed since January 1, 2000."
|
| 301 |
+
}
|
| 302 |
+
]
|
| 303 |
+
},
|
| 304 |
+
{
|
| 305 |
+
"block_name": "(username)",
|
| 306 |
+
"block_type": "Sensing",
|
| 307 |
+
"op_code": "sensing_username",
|
| 308 |
+
"block_shape": "Reporter Block",
|
| 309 |
+
"functionality": "Reports the username of the user currently logged into Scratch. If no user is logged in, it reports nothing.",
|
| 310 |
+
"inputs": null,
|
| 311 |
+
"example_standalone": "username",
|
| 312 |
+
"example_with_other_blocks": [
|
| 313 |
+
{
|
| 314 |
+
"script": "say join [Hello, ] (username)",
|
| 315 |
+
"explanation": "This script makes the sprite greet the user by their Scratch username."
|
| 316 |
+
}
|
| 317 |
+
]
|
| 318 |
+
},
|
| 319 |
+
{
|
| 320 |
+
"block_name": "(() + ())",
|
| 321 |
+
"block_type": "operator",
|
| 322 |
+
"op_code": "operator_add",
|
| 323 |
+
"block_shape": "Reporter Block",
|
| 324 |
+
"functionality": "Adds two numerical values.",
|
| 325 |
+
"inputs": [
|
| 326 |
+
{
|
| 327 |
+
"name": "number1",
|
| 328 |
+
"type": "number"
|
| 329 |
+
},
|
| 330 |
+
{
|
| 331 |
+
"name": "number2",
|
| 332 |
+
"type": "number"
|
| 333 |
+
}
|
| 334 |
+
],
|
| 335 |
+
"example_standalone": "(5) + (3)",
|
| 336 |
+
"example_with_other_blocks": [
|
| 337 |
+
{
|
| 338 |
+
"script": "set [total v] to ( (number 1) + (number 2) )",
|
| 339 |
+
"explanation": "This script calculates the sum of 'number 1' and 'number 2' and stores the result in the 'total' variable."
|
| 340 |
+
}
|
| 341 |
+
]
|
| 342 |
+
},
|
| 343 |
+
{
|
| 344 |
+
"block_name": "(() - ())",
|
| 345 |
+
"block_type": "operator",
|
| 346 |
+
"op_code": "operator_subtract",
|
| 347 |
+
"block_shape": "Reporter Block",
|
| 348 |
+
"functionality": "Subtracts the second numerical value from the first.",
|
| 349 |
+
"inputs": [
|
| 350 |
+
{
|
| 351 |
+
"name": "number1",
|
| 352 |
+
"type": "number"
|
| 353 |
+
},
|
| 354 |
+
{
|
| 355 |
+
"name": "number2",
|
| 356 |
+
"type": "number"
|
| 357 |
+
}
|
| 358 |
+
],
|
| 359 |
+
"example_standalone": "((10) - (4))",
|
| 360 |
+
"example_with_other_blocks": [
|
| 361 |
+
{
|
| 362 |
+
"script": "set [difference v] to ( (number 1) - (number 2) )",
|
| 363 |
+
"explanation": "This script calculates the subtraction of 'number 2' from 'number 1' and stores the result in the 'difference' variable."
|
| 364 |
+
}
|
| 365 |
+
]
|
| 366 |
+
},
|
| 367 |
+
{
|
| 368 |
+
"block_name": "(() * ())",
|
| 369 |
+
"block_type": "operator",
|
| 370 |
+
"op_code": "operator_multiply",
|
| 371 |
+
"block_shape": "Reporter Block",
|
| 372 |
+
"functionality": "Multiplies two numerical values.",
|
| 373 |
+
"inputs": [
|
| 374 |
+
{
|
| 375 |
+
"name": "number1",
|
| 376 |
+
"type": "number"
|
| 377 |
+
},
|
| 378 |
+
{
|
| 379 |
+
"name": "number2",
|
| 380 |
+
"type": "number"
|
| 381 |
+
}
|
| 382 |
+
],
|
| 383 |
+
"example_standalone": "(6) * (7)",
|
| 384 |
+
"example_with_other_blocks": [
|
| 385 |
+
{
|
| 386 |
+
"script": "set [area v] to ( (length) * (width) )",
|
| 387 |
+
"explanation": "This script calculates the area by multiplying 'length' and 'width' variables and stores it in the 'area' variable."
|
| 388 |
+
}
|
| 389 |
+
]
|
| 390 |
+
},
|
| 391 |
+
{
|
| 392 |
+
"block_name": "(() / ())",
|
| 393 |
+
"block_type": "operator",
|
| 394 |
+
"op_code": "operator_divide",
|
| 395 |
+
"block_shape": "Reporter Block",
|
| 396 |
+
"functionality": "Divides the first numerical value by the second.",
|
| 397 |
+
"inputs": [
|
| 398 |
+
{
|
| 399 |
+
"name": "number1",
|
| 400 |
+
"type": "number"
|
| 401 |
+
},
|
| 402 |
+
{
|
| 403 |
+
"name": "number2",
|
| 404 |
+
"type": "number"
|
| 405 |
+
}
|
| 406 |
+
],
|
| 407 |
+
"example_standalone": "((20) / (5))",
|
| 408 |
+
"example_with_other_blocks": [
|
| 409 |
+
{
|
| 410 |
+
"script": "set [average v] to ( (total score) / (number of students) )",
|
| 411 |
+
"explanation": "This script calculates the average by dividing 'total score' by 'number of students' and stores it in the 'average' variable."
|
| 412 |
+
}
|
| 413 |
+
]
|
| 414 |
+
},
|
| 415 |
+
{
|
| 416 |
+
"block_name": "(pick random () to ())",
|
| 417 |
+
"block_type": "operator",
|
| 418 |
+
"op_code": "operator_random",
|
| 419 |
+
"block_shape": "Reporter Block",
|
| 420 |
+
"functionality": "Generates a random integer within a specified inclusive range.",
|
| 421 |
+
"inputs": [
|
| 422 |
+
{
|
| 423 |
+
"name": "min",
|
| 424 |
+
"type": "number"
|
| 425 |
+
},
|
| 426 |
+
{
|
| 427 |
+
"name": "max",
|
| 428 |
+
"type": "number"
|
| 429 |
+
}
|
| 430 |
+
],
|
| 431 |
+
"example_standalone": "(pick random (1) to (10))",
|
| 432 |
+
"example_with_other_blocks": [
|
| 433 |
+
{
|
| 434 |
+
"script": "go to x: (pick random -240 to 240) y: (pick random -180 to 180)",
|
| 435 |
+
"explanation": "This script moves the sprite to a random position on the stage."
|
| 436 |
+
}
|
| 437 |
+
]
|
| 438 |
+
},
|
| 439 |
+
{
|
| 440 |
+
"block_name": "(join ()())",
|
| 441 |
+
"block_type": "operator",
|
| 442 |
+
"op_code": "operator_join",
|
| 443 |
+
"block_shape": "Reporter Block",
|
| 444 |
+
"functionality": "Concatenates two strings or values into a single string.",
|
| 445 |
+
"inputs": [
|
| 446 |
+
{
|
| 447 |
+
"name": "string1",
|
| 448 |
+
"type": "string/number"
|
| 449 |
+
},
|
| 450 |
+
{
|
| 451 |
+
"name": "string2",
|
| 452 |
+
"type": "string/number"
|
| 453 |
+
}
|
| 454 |
+
],
|
| 455 |
+
"example_standalone": "(join [Hello ][World!])",
|
| 456 |
+
"example_with_other_blocks": [
|
| 457 |
+
{
|
| 458 |
+
"script": "say (join [Hello ][World!])",
|
| 459 |
+
"explanation": "This script makes the sprite display 'Hello World!' in a speech bubble by joining two string literals."
|
| 460 |
+
}
|
| 461 |
+
]
|
| 462 |
+
},
|
| 463 |
+
{
|
| 464 |
+
"block_name": "letter () of ()",
|
| 465 |
+
"block_type": "operator",
|
| 466 |
+
"op_code": "operator_letterof",
|
| 467 |
+
"block_shape": "Reporter Block",
|
| 468 |
+
"functionality": "Reports the character at a specific numerical position within a string.",
|
| 469 |
+
"inputs": [
|
| 470 |
+
{
|
| 471 |
+
"name": "index",
|
| 472 |
+
"type": "number"
|
| 473 |
+
},
|
| 474 |
+
{
|
| 475 |
+
"name": "text",
|
| 476 |
+
"type": "string"
|
| 477 |
+
}
|
| 478 |
+
],
|
| 479 |
+
"example_standalone": "(letter (1) of [apple])",
|
| 480 |
+
"example_with_other_blocks": [
|
| 481 |
+
{
|
| 482 |
+
"script": "say (letter (1) of [apple])",
|
| 483 |
+
"explanation": "This script makes the sprite display the first character of the string 'apple', which is 'a'."
|
| 484 |
+
}
|
| 485 |
+
]
|
| 486 |
+
},
|
| 487 |
+
{
|
| 488 |
+
"block_name": "(length of ())",
|
| 489 |
+
"block_type": "operator",
|
| 490 |
+
"op_code": "operator_length",
|
| 491 |
+
"block_shape": "Reporter Block",
|
| 492 |
+
"functionality": "Reports the total number of characters in a given string.",
|
| 493 |
+
"inputs": [
|
| 494 |
+
{
|
| 495 |
+
"name": "text",
|
| 496 |
+
"type": "string"
|
| 497 |
+
}
|
| 498 |
+
],
|
| 499 |
+
"example_standalone": "(length of [banana])",
|
| 500 |
+
"example_with_other_blocks": [
|
| 501 |
+
{
|
| 502 |
+
"script": "say (length of [banana])",
|
| 503 |
+
"explanation": "This script makes the sprite display the length of the string 'banana', which is 6."
|
| 504 |
+
}
|
| 505 |
+
]
|
| 506 |
+
},
|
| 507 |
+
{
|
| 508 |
+
"block_name": "(() mod ())",
|
| 509 |
+
"block_type": "operator",
|
| 510 |
+
"op_code": "operator_mod",
|
| 511 |
+
"block_shape": "Reporter Block",
|
| 512 |
+
"functionality": "Reports the remainder when the first number is divided by the second.",
|
| 513 |
+
"inputs": [
|
| 514 |
+
{
|
| 515 |
+
"name": "number1",
|
| 516 |
+
"type": "number"
|
| 517 |
+
},
|
| 518 |
+
{
|
| 519 |
+
"name": "number2",
|
| 520 |
+
"type": "number"
|
| 521 |
+
}
|
| 522 |
+
],
|
| 523 |
+
"example_standalone": "((10) mod (3))",
|
| 524 |
+
"example_with_other_blocks": [
|
| 525 |
+
{
|
| 526 |
+
"script": "if <([number v] mod (2) = (0))> then\n say [Even number]",
|
| 527 |
+
"explanation": "This script checks if a 'number' variable is even by checking if its remainder when divided by 2 is 0."
|
| 528 |
+
}
|
| 529 |
+
]
|
| 530 |
+
},
|
| 531 |
+
{
|
| 532 |
+
"block_name": "(round ())",
|
| 533 |
+
"block_type": "operator",
|
| 534 |
+
"op_code": "operator_round",
|
| 535 |
+
"block_shape": "Reporter Block",
|
| 536 |
+
"functionality": "Rounds a numerical value to the nearest integer.",
|
| 537 |
+
"inputs": [
|
| 538 |
+
{
|
| 539 |
+
"name": "number",
|
| 540 |
+
"type": "number"
|
| 541 |
+
}
|
| 542 |
+
],
|
| 543 |
+
"example_standalone": "(round (3.7))",
|
| 544 |
+
"example_with_other_blocks": [
|
| 545 |
+
{
|
| 546 |
+
"script": "set [rounded score v] to (round (score))",
|
| 547 |
+
"explanation": "This script rounds the 'score' variable to the nearest whole number and stores it in 'rounded score'."
|
| 548 |
+
}
|
| 549 |
+
]
|
| 550 |
+
},
|
| 551 |
+
{
|
| 552 |
+
"block_name": "(() of ())",
|
| 553 |
+
"block_type": "operator",
|
| 554 |
+
"op_code": "operator_mathop",
|
| 555 |
+
"block_shape": "Reporter Block",
|
| 556 |
+
"functionality": "Performs various mathematical functions (e.g., absolute value, square root, trigonometric functions).",
|
| 557 |
+
"inputs": [
|
| 558 |
+
{
|
| 559 |
+
"name": "function type",
|
| 560 |
+
"type": "dropdown",
|
| 561 |
+
"options": [
|
| 562 |
+
"abs",
|
| 563 |
+
"floor",
|
| 564 |
+
"ceiling",
|
| 565 |
+
"sqrt",
|
| 566 |
+
"sin",
|
| 567 |
+
"cos",
|
| 568 |
+
"tan",
|
| 569 |
+
"asin",
|
| 570 |
+
"acos",
|
| 571 |
+
"atan",
|
| 572 |
+
"ln",
|
| 573 |
+
"log",
|
| 574 |
+
"e ^",
|
| 575 |
+
"10 ^"
|
| 576 |
+
]
|
| 577 |
+
},
|
| 578 |
+
{
|
| 579 |
+
"name": "value",
|
| 580 |
+
"type": "number"
|
| 581 |
+
}
|
| 582 |
+
],
|
| 583 |
+
"example_standalone": "([sqrt v] of (25))",
|
| 584 |
+
"example_with_other_blocks": [
|
| 585 |
+
{
|
| 586 |
+
"script": "set [distance v] to ([sqrt v] of ( ( (x position) * (x position) ) + ( (y position) * (y position) ) ))",
|
| 587 |
+
"explanation": "This script calculates the distance from the origin (0,0) using the Pythagorean theorem and stores it in 'distance'."
|
| 588 |
+
}
|
| 589 |
+
]
|
| 590 |
+
},
|
| 591 |
+
{
|
| 592 |
+
"block_name": "[variable v]",
|
| 593 |
+
"block_type": "Data",
|
| 594 |
+
"op_code": "data_variable",
|
| 595 |
+
"block_shape": "Reporter Block",
|
| 596 |
+
"functionality": "Provides the current value stored in a variable.",
|
| 597 |
+
"inputs": [
|
| 598 |
+
{
|
| 599 |
+
"name": "variable name",
|
| 600 |
+
"type": "dropdown",
|
| 601 |
+
"options": ["my variable", "score", "..."]
|
| 602 |
+
}
|
| 603 |
+
],
|
| 604 |
+
"example_standalone": "[score v]",
|
| 605 |
+
"example_with_other_blocks": [
|
| 606 |
+
{
|
| 607 |
+
"script": "say ([score v]) for (2) seconds",
|
| 608 |
+
"explanation": "This script makes the sprite say the current value of the 'score' variable for 2 seconds."
|
| 609 |
+
}
|
| 610 |
+
]
|
| 611 |
+
},
|
| 612 |
+
{
|
| 613 |
+
"block_name": "[list v]",
|
| 614 |
+
"block_type": "Data",
|
| 615 |
+
"op_code": "data_list",
|
| 616 |
+
"block_shape": "Reporter Block",
|
| 617 |
+
"functionality": "Reports the entire content of a specified list. When clicked in the editor, it displays the list as a monitor.",
|
| 618 |
+
"inputs": [
|
| 619 |
+
{
|
| 620 |
+
"name": "list name",
|
| 621 |
+
"type": "dropdown",
|
| 622 |
+
"options": ["my list", "list2", "..."]
|
| 623 |
+
}
|
| 624 |
+
],
|
| 625 |
+
"example_standalone": "[my list v]",
|
| 626 |
+
"example_with_other_blocks": [
|
| 627 |
+
{
|
| 628 |
+
"script": "say ([my list v]) ",
|
| 629 |
+
"explanation": "This script makes the sprite say all the contents of 'my list'."
|
| 630 |
+
}
|
| 631 |
+
]
|
| 632 |
+
},
|
| 633 |
+
{
|
| 634 |
+
"block_name": "(item (2) of [myList v])",
|
| 635 |
+
"block_type": "Data",
|
| 636 |
+
"op_code": "data_itemoflist",
|
| 637 |
+
"block_shape": "Reporter Block",
|
| 638 |
+
"functionality": "Reports the item located at a specific position in a list.",
|
| 639 |
+
"inputs": [
|
| 640 |
+
{
|
| 641 |
+
"name": "index/option",
|
| 642 |
+
"type": "number or dropdown",
|
| 643 |
+
"options": [
|
| 644 |
+
"last",
|
| 645 |
+
"random"
|
| 646 |
+
]
|
| 647 |
+
},
|
| 648 |
+
{
|
| 649 |
+
"name": "list name",
|
| 650 |
+
"type": "dropdown",
|
| 651 |
+
"options": ["shopping list", "my list", "..."]
|
| 652 |
+
}
|
| 653 |
+
],
|
| 654 |
+
"example_standalone": "item (1) of [shopping list v]",
|
| 655 |
+
"example_with_other_blocks": [
|
| 656 |
+
{
|
| 657 |
+
"script": "say (item (2) of [myList v]) for 2 seconds ",
|
| 658 |
+
"explanation": "This script makes the sprite display the first item from the 'shopping list'."
|
| 659 |
+
}
|
| 660 |
+
]
|
| 661 |
+
},
|
| 662 |
+
{
|
| 663 |
+
"block_name": "(length of [myList v])",
|
| 664 |
+
"block_type": "Data",
|
| 665 |
+
"op_code": "data_lengthoflist",
|
| 666 |
+
"block_shape": "Reporter Block",
|
| 667 |
+
"functionality": "Provides the total number of items contained in a list.",
|
| 668 |
+
"inputs": [
|
| 669 |
+
{
|
| 670 |
+
"name": "list name",
|
| 671 |
+
"type": "dropdown",
|
| 672 |
+
"options": ["my list", "shopping list", "..."]
|
| 673 |
+
}
|
| 674 |
+
],
|
| 675 |
+
"example_standalone": "(length of [myList v])",
|
| 676 |
+
"example_with_other_blocks": [
|
| 677 |
+
{
|
| 678 |
+
"script": "say join (length of [shopping list v]) [ items in the list.]",
|
| 679 |
+
"explanation": "This script makes the sprite display the total number of items currently in the 'shopping list'."
|
| 680 |
+
}
|
| 681 |
+
]
|
| 682 |
+
},
|
| 683 |
+
{
|
| 684 |
+
"block_name": "(item # of [Dog] in [myList v])",
|
| 685 |
+
"block_type": "Data",
|
| 686 |
+
"op_code": "data_itemnumoflist",
|
| 687 |
+
"block_shape": "Reporter Block",
|
| 688 |
+
"functionality": "Reports the index number of the first occurrence of a specified item in a list. If the item is not found, it reports 0.",
|
| 689 |
+
"inputs": [
|
| 690 |
+
{
|
| 691 |
+
"name": "item",
|
| 692 |
+
"type": "string/number"
|
| 693 |
+
},
|
| 694 |
+
{
|
| 695 |
+
"name": "list name",
|
| 696 |
+
"type": "dropdown",
|
| 697 |
+
"options": ["my list", "shopping list", "..."]
|
| 698 |
+
}
|
| 699 |
+
],
|
| 700 |
+
"example_standalone": "(item # of [apple] in [shopping list v])",
|
| 701 |
+
"example_with_other_blocks": [
|
| 702 |
+
{
|
| 703 |
+
"script": "if <(item # of [Dog] in [myList v])> (0)> then\n say join [Dog found at position ] (item # of [Dog] in [my list v])",
|
| 704 |
+
"explanation": "This script checks if 'banana' is in 'my list' and, if so, reports its position."
|
| 705 |
+
}
|
| 706 |
+
]
|
| 707 |
+
}
|
| 708 |
+
]
|
| 709 |
+
}
|
blocks/stack_blocks.json
ADDED
|
@@ -0,0 +1,1321 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"block_category": "Stack Blocks",
|
| 3 |
+
"description": "Stack blocks are the most common block shape, featuring a notch at the top and a bump at the bottom. They perform the main commands within a script and can connect both above and below them.",
|
| 4 |
+
"blocks": [
|
| 5 |
+
{
|
| 6 |
+
"block_name": "move () steps",
|
| 7 |
+
"block_type": "Motion",
|
| 8 |
+
"block_shape": "Stack Block",
|
| 9 |
+
"op_code": "motion_movesteps",
|
| 10 |
+
"functionality": "Moves the sprite forward by the specified number of steps in the direction it is currently facing. A positive value moves it forward, and a negative value moves it backward.",
|
| 11 |
+
"inputs": [
|
| 12 |
+
{
|
| 13 |
+
"name": "STEPS",
|
| 14 |
+
"type": "number"
|
| 15 |
+
}
|
| 16 |
+
],
|
| 17 |
+
"example_standalone": "move () steps",
|
| 18 |
+
"example_with_other_blocks": [
|
| 19 |
+
{
|
| 20 |
+
"script": "when green flag clicked\n go to x: (0) y: (0)\n point in direction (90)\n move (50) steps\nend",
|
| 21 |
+
"explanation": "This script first places the sprite at the center of the stage, points it to the right (90 degrees), and then moves it 50 steps in that direction."
|
| 22 |
+
}
|
| 23 |
+
]
|
| 24 |
+
},
|
| 25 |
+
{
|
| 26 |
+
"block_name": "turn right () degrees",
|
| 27 |
+
"block_type": "Motion",
|
| 28 |
+
"block_shape": "Stack Block",
|
| 29 |
+
"op_code": "motion_turnright",
|
| 30 |
+
"functionality": "Turns the sprite clockwise by the specified number of degrees.",
|
| 31 |
+
"inputs": [
|
| 32 |
+
{
|
| 33 |
+
"name": "DEGREES",
|
| 34 |
+
"type": "number"
|
| 35 |
+
}
|
| 36 |
+
],
|
| 37 |
+
"example_standalone": "turn (clockwise icon) (15) degrees",
|
| 38 |
+
"example_with_other_blocks": [
|
| 39 |
+
{
|
| 40 |
+
"script": "when [right arrow v] key pressed\n turn (clockwise icon) (15) degrees\nend",
|
| 41 |
+
"explanation": "This script makes the sprite turn clockwise by 15 degrees every time the right arrow key is pressed."
|
| 42 |
+
},
|
| 43 |
+
{
|
| 44 |
+
"script": "when green flag clicked\n forever\n turn (clockwise icon) (15) degrees\n wait (0.5) seconds\n end",
|
| 45 |
+
"explanation": "This script makes the sprite continuously spin clockwise by 15 degrees every half second."
|
| 46 |
+
}
|
| 47 |
+
]
|
| 48 |
+
},
|
| 49 |
+
{
|
| 50 |
+
"block_name": "turn left () degrees",
|
| 51 |
+
"block_type": "Motion",
|
| 52 |
+
"block_shape": "Stack Block",
|
| 53 |
+
"op_code": "motion_turnleft",
|
| 54 |
+
"functionality": "Turns the sprite counter-clockwise by the specified number of degrees.",
|
| 55 |
+
"inputs": [
|
| 56 |
+
{
|
| 57 |
+
"name": "DEGREES",
|
| 58 |
+
"type": "number"
|
| 59 |
+
}
|
| 60 |
+
],
|
| 61 |
+
"example_standalone": "turn (counter-clockwise icon) (15) degrees",
|
| 62 |
+
"example_with_other_blocks": [
|
| 63 |
+
{
|
| 64 |
+
"script": "when [left arrow v] key pressed\n turn (counter-clockwise icon) (15) degrees\nend",
|
| 65 |
+
"explanation": "This script makes the sprite turn counter-clockwise by 15 degrees every time the left arrow key is pressed."
|
| 66 |
+
},
|
| 67 |
+
{
|
| 68 |
+
"script": "when green flag clicked\n forever\n turn (counter-clockwise icon) (15) degrees\n wait (0.5) seconds\n end\nend",
|
| 69 |
+
"explanation": "This script makes the sprite continuously spin counter-clockwise by 15 degrees every half second."
|
| 70 |
+
}
|
| 71 |
+
]
|
| 72 |
+
},
|
| 73 |
+
{
|
| 74 |
+
"block_name": "go to ()",
|
| 75 |
+
"block_type": "Motion",
|
| 76 |
+
"block_shape": "Stack Block",
|
| 77 |
+
"op_code": "motion_goto",
|
| 78 |
+
"functionality": "Moves the sprite to a specified location, which can be a random position or at the mouse pointer or another to the sprite.",
|
| 79 |
+
"inputs": [
|
| 80 |
+
{
|
| 81 |
+
"name": "TO",
|
| 82 |
+
"type": "dropdown",
|
| 83 |
+
"options": [
|
| 84 |
+
"random position",
|
| 85 |
+
"mouse-pointer",
|
| 86 |
+
"sprite1",
|
| 87 |
+
"..."
|
| 88 |
+
]
|
| 89 |
+
}
|
| 90 |
+
],
|
| 91 |
+
"example_standalone": "go to [random position v]",
|
| 92 |
+
"example_with_other_blocks": [
|
| 93 |
+
{
|
| 94 |
+
"script": "when this sprite clicked\n go to [mouse-pointer v]",
|
| 95 |
+
"explanation": "This script moves the sprite to the current position of the mouse pointer whenever the sprite is clicked."
|
| 96 |
+
},
|
| 97 |
+
{
|
| 98 |
+
"script": "when this sprite clicked\n go to [sprite v]",
|
| 99 |
+
"explanation": "This script moves the sprite to the another sprite's position whenever the sprite is clicked."
|
| 100 |
+
}
|
| 101 |
+
]
|
| 102 |
+
},
|
| 103 |
+
{
|
| 104 |
+
"block_name": "go to x: () y: ()",
|
| 105 |
+
"block_type": "Motion",
|
| 106 |
+
"block_shape": "Stack Block",
|
| 107 |
+
"op_code": "motion_gotoxy",
|
| 108 |
+
"functionality": "Moves the sprite to the specified X and Y coordinates on the stage.",
|
| 109 |
+
"inputs": [
|
| 110 |
+
{
|
| 111 |
+
"name": "X",
|
| 112 |
+
"type": "number"
|
| 113 |
+
},
|
| 114 |
+
{
|
| 115 |
+
"name": "Y",
|
| 116 |
+
"type": "number"
|
| 117 |
+
}
|
| 118 |
+
],
|
| 119 |
+
"example_standalone": "go to x: (0) y: (0)",
|
| 120 |
+
"example_with_other_blocks": [
|
| 121 |
+
{
|
| 122 |
+
"script": "when green flag clicked\n go to x: (120) y: (0)\n say [Ready to start! v] for (1) seconds\nend",
|
| 123 |
+
"explanation": "This script positions the sprite at the center of the stage at the beginning of the project and then makes it say 'Ready to start!'."
|
| 124 |
+
}
|
| 125 |
+
]
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
"block_name": "glide () secs to ()",
|
| 129 |
+
"block_type": "Motion",
|
| 130 |
+
"block_shape": "Stack Block",
|
| 131 |
+
"op_code": "motion_glideto",
|
| 132 |
+
"functionality": "Glides the sprite smoothly to a specified location (random position, mouse pointer, or another sprite) over a given number of seconds.",
|
| 133 |
+
"inputs": [
|
| 134 |
+
{
|
| 135 |
+
"name": "SECS",
|
| 136 |
+
"type": "number"
|
| 137 |
+
},
|
| 138 |
+
{
|
| 139 |
+
"name": "TO",
|
| 140 |
+
"type": "dropdown",
|
| 141 |
+
"options": [
|
| 142 |
+
"random position",
|
| 143 |
+
"mouse-pointer",
|
| 144 |
+
"sprite1",
|
| 145 |
+
"sprite2",
|
| 146 |
+
"..."
|
| 147 |
+
]
|
| 148 |
+
}
|
| 149 |
+
],
|
| 150 |
+
"example_standalone": "glide (1) secs to ([random position v])",
|
| 151 |
+
"example_with_other_blocks": [
|
| 152 |
+
{
|
| 153 |
+
"script": "when green flag clicked\n glide (1) secs to ([mouse-pointer v])\nend",
|
| 154 |
+
"explanation": "This script makes the sprite glide smoothly to the mouse pointer's position over 1 second when the green flag is clicked."
|
| 155 |
+
}
|
| 156 |
+
]
|
| 157 |
+
},
|
| 158 |
+
{
|
| 159 |
+
"block_name": "glide () secs to x: () y: ()",
|
| 160 |
+
"block_type": "Motion",
|
| 161 |
+
"block_shape": "Stack Block",
|
| 162 |
+
"op_code": "motion_glidesecstoxy",
|
| 163 |
+
"functionality": "Glides the sprite smoothly to the specified X and Y coordinates over a given number of seconds.",
|
| 164 |
+
"inputs": [
|
| 165 |
+
{
|
| 166 |
+
"name": "SECS",
|
| 167 |
+
"type": "number"
|
| 168 |
+
},
|
| 169 |
+
{
|
| 170 |
+
"name": "X",
|
| 171 |
+
"type": "number"
|
| 172 |
+
},
|
| 173 |
+
{
|
| 174 |
+
"name": "Y",
|
| 175 |
+
"type": "number"
|
| 176 |
+
}
|
| 177 |
+
],
|
| 178 |
+
"example_standalone": "glide (1) secs to x: (100) y: (50)",
|
| 179 |
+
"example_with_other_blocks": [
|
| 180 |
+
{
|
| 181 |
+
"script": "when green flag clicked\n glide (2) secs to x: (150) y: (-100)\n glide (2) secs to x: (-150) y: (100)\nend",
|
| 182 |
+
"explanation": "This script makes the sprite glide to two different points on the stage, taking 2 seconds for each movement."
|
| 183 |
+
}
|
| 184 |
+
]
|
| 185 |
+
},
|
| 186 |
+
{
|
| 187 |
+
"block_name": "point in direction ()",
|
| 188 |
+
"block_type": "Motion",
|
| 189 |
+
"block_shape": "Stack Block",
|
| 190 |
+
"op_code": "motion_pointindirection",
|
| 191 |
+
"functionality": "Sets the sprite's direction to a specified angle in degrees (0 = up, 90 = right, 180 = down, -90 = left).",
|
| 192 |
+
"inputs": [
|
| 193 |
+
{
|
| 194 |
+
"name": "DIRECTION",
|
| 195 |
+
"type": "number"
|
| 196 |
+
}
|
| 197 |
+
],
|
| 198 |
+
"example_standalone": "point in direction (90)",
|
| 199 |
+
"example_with_other_blocks": [
|
| 200 |
+
{
|
| 201 |
+
"script": "when green flag clicked\n point in direction (0)\n move (100) steps\nend",
|
| 202 |
+
"explanation": "This script makes the sprite point upwards (0 degrees) and then move 100 steps in that direction."
|
| 203 |
+
}
|
| 204 |
+
]
|
| 205 |
+
},
|
| 206 |
+
{
|
| 207 |
+
"block_name": "point towards ()",
|
| 208 |
+
"block_type": "Motion",
|
| 209 |
+
"block_shape": "Stack Block",
|
| 210 |
+
"op_code": "motion_pointtowards",
|
| 211 |
+
"functionality": "Points the sprite towards the mouse pointer or another specified sprite.",
|
| 212 |
+
"inputs": [
|
| 213 |
+
{
|
| 214 |
+
"name": "TOWARDS",
|
| 215 |
+
"type": "dropdown",
|
| 216 |
+
"options": [
|
| 217 |
+
"mouse-pointer",
|
| 218 |
+
"sprite1",
|
| 219 |
+
"..."
|
| 220 |
+
]
|
| 221 |
+
}
|
| 222 |
+
],
|
| 223 |
+
"example_standalone": "point towards [mouse-pointer v]",
|
| 224 |
+
"example_with_other_blocks": [
|
| 225 |
+
{
|
| 226 |
+
"script": "when this sprite clicked\n point towards [mouse-pointer v]\n move (10) steps\nend",
|
| 227 |
+
"explanation": "When the sprite is clicked, it will point towards the mouse pointer and then move 10 steps in that direction."
|
| 228 |
+
},
|
| 229 |
+
{
|
| 230 |
+
"script": "when green flag clicked\n forever\n point towards [mouse-pointer v]\n move (5) steps\n end\nend",
|
| 231 |
+
"explanation": "This script makes the sprite continuously follow the mouse pointer."
|
| 232 |
+
}
|
| 233 |
+
]
|
| 234 |
+
},
|
| 235 |
+
{
|
| 236 |
+
"block_name": "change x by ()",
|
| 237 |
+
"block_type": "Motion",
|
| 238 |
+
"block_shape": "Stack Block",
|
| 239 |
+
"op_code": "motion_changexby",
|
| 240 |
+
"functionality": "Changes the sprite's X-coordinate by the specified amount, moving it horizontally.",
|
| 241 |
+
"inputs": [
|
| 242 |
+
{
|
| 243 |
+
"name": "DX",
|
| 244 |
+
"type": "number"
|
| 245 |
+
}
|
| 246 |
+
],
|
| 247 |
+
"example_standalone": "change x by (10)",
|
| 248 |
+
"example_with_other_blocks": [
|
| 249 |
+
{
|
| 250 |
+
"script": "when [right arrow v] key pressed\n change x by (10)\nend",
|
| 251 |
+
"explanation": "This script moves the sprite 10 steps to the right when the right arrow key is pressed."
|
| 252 |
+
}
|
| 253 |
+
]
|
| 254 |
+
},
|
| 255 |
+
{
|
| 256 |
+
"block_name": "set x to ()",
|
| 257 |
+
"block_type": "Motion",
|
| 258 |
+
"block_shape": "Stack Block",
|
| 259 |
+
"op_code": "motion_setx",
|
| 260 |
+
"functionality": "Sets the sprite's X-coordinate to a specific value, placing it at a precise horizontal position.",
|
| 261 |
+
"inputs": [
|
| 262 |
+
{
|
| 263 |
+
"name": "X",
|
| 264 |
+
"type": "number"
|
| 265 |
+
}
|
| 266 |
+
],
|
| 267 |
+
"example_standalone": "set x to (0)",
|
| 268 |
+
"example_with_other_blocks": [
|
| 269 |
+
{
|
| 270 |
+
"script": "when green flag clicked\n set x to (0)\n set y to (0)\nend",
|
| 271 |
+
"explanation": "This script centers the sprite horizontally at the start of the project."
|
| 272 |
+
}
|
| 273 |
+
]
|
| 274 |
+
},
|
| 275 |
+
{
|
| 276 |
+
"block_name": "change y by ()",
|
| 277 |
+
"block_type": "Motion",
|
| 278 |
+
"block_shape": "Stack Block",
|
| 279 |
+
"op_code": "motion_changeyby",
|
| 280 |
+
"functionality": "Changes the sprite's Y-coordinate by the specified amount, moving it vertically.",
|
| 281 |
+
"inputs": [
|
| 282 |
+
{
|
| 283 |
+
"name": "DY",
|
| 284 |
+
"type": "number"
|
| 285 |
+
}
|
| 286 |
+
],
|
| 287 |
+
"example_standalone": "change y by (10)",
|
| 288 |
+
"example_with_other_blocks": [
|
| 289 |
+
{
|
| 290 |
+
"script": "when [up arrow v] key pressed\n change y by (10)\nend",
|
| 291 |
+
"explanation": "This script moves the sprite 10 steps up when the up arrow key is pressed."
|
| 292 |
+
}
|
| 293 |
+
]
|
| 294 |
+
},
|
| 295 |
+
{
|
| 296 |
+
"block_name": "set y to ()",
|
| 297 |
+
"block_type": "Motion",
|
| 298 |
+
"block_shape": "Stack Block",
|
| 299 |
+
"op_code": "motion_sety",
|
| 300 |
+
"functionality": "Sets the sprite's Y-coordinate to a specific value, placing it at a precise vertical position.",
|
| 301 |
+
"inputs": [
|
| 302 |
+
{
|
| 303 |
+
"name": "Y",
|
| 304 |
+
"type": "number"
|
| 305 |
+
}
|
| 306 |
+
],
|
| 307 |
+
"example_standalone": "set y to (0)",
|
| 308 |
+
"example_with_other_blocks": [
|
| 309 |
+
{
|
| 310 |
+
"script": "when green flag clicked\n set x to (0)\n set y to (0)\nend",
|
| 311 |
+
"explanation": "This script centers the sprite vertically at the start of the project."
|
| 312 |
+
}
|
| 313 |
+
]
|
| 314 |
+
},
|
| 315 |
+
{
|
| 316 |
+
"block_name": "if on edge, bounce",
|
| 317 |
+
"block_type": "Motion",
|
| 318 |
+
"block_shape": "Stack Block",
|
| 319 |
+
"op_code": "motion_ifonedgebounce",
|
| 320 |
+
"functionality": "Reverses the sprite's direction if it touches the edge of the stage.",
|
| 321 |
+
"inputs": null,
|
| 322 |
+
"example_standalone": "if on edge, bounce",
|
| 323 |
+
"example_with_other_blocks": [
|
| 324 |
+
{
|
| 325 |
+
"script": "when I receive [start moving v]\n repeat (50)\n move (5) steps\n if on edge, bounce\n end\nend",
|
| 326 |
+
"explanation": "Upon receiving the 'start moving' broadcast, the sprite will move 5 steps repeatedly for 50 times, bouncing off edges if it touches them."
|
| 327 |
+
},
|
| 328 |
+
{
|
| 329 |
+
"script": "when green flag clicked\n forever\n move (10) steps\n if on edge, bounce\n end\nend",
|
| 330 |
+
"explanation": "This script makes the sprite move continuously and bounce off the edges of the stage."
|
| 331 |
+
}
|
| 332 |
+
]
|
| 333 |
+
},
|
| 334 |
+
{
|
| 335 |
+
"block_name": "set rotation style ()",
|
| 336 |
+
"block_type": "Motion",
|
| 337 |
+
"block_shape": "Stack Block",
|
| 338 |
+
"op_code": "motion_setrotationstyle",
|
| 339 |
+
"functionality": "Determines how the sprite rotates: 'left-right' (flips horizontally), 'don't rotate' (stays facing one direction), or 'all around' (rotates freely).",
|
| 340 |
+
"inputs": [
|
| 341 |
+
{
|
| 342 |
+
"name": "STYLE",
|
| 343 |
+
"type": "dropdown",
|
| 344 |
+
"options": [
|
| 345 |
+
"left-right",
|
| 346 |
+
"don't rotate",
|
| 347 |
+
"all around"
|
| 348 |
+
]
|
| 349 |
+
}
|
| 350 |
+
],
|
| 351 |
+
"example_standalone": "set rotation style [left-right v]",
|
| 352 |
+
"example_with_other_blocks": [
|
| 353 |
+
{
|
| 354 |
+
"script": "when backdrop switches to [game level 1 v]\n set rotation style [all around v]\nend",
|
| 355 |
+
"explanation": "When the backdrop changes to 'game level 1', the sprite's rotation style will be set to 'all around', allowing it to rotate freely."
|
| 356 |
+
},
|
| 357 |
+
{
|
| 358 |
+
"script": "when green flag clicked\n set rotation style [left-right v]\n forever\n move (10) steps\n if on edge, bounce\n end \nend",
|
| 359 |
+
"explanation": "This script makes the sprite move horizontally and flip its costume when it hits an edge, instead of rotating."
|
| 360 |
+
}
|
| 361 |
+
]
|
| 362 |
+
},
|
| 363 |
+
{
|
| 364 |
+
"block_name": "say () for () seconds",
|
| 365 |
+
"block_type": "Looks",
|
| 366 |
+
"block_shape": "Stack Block",
|
| 367 |
+
"op_code": "looks_sayforsecs",
|
| 368 |
+
"functionality": "Displays a speech bubble containing specified text for a set duration.",
|
| 369 |
+
"inputs": [
|
| 370 |
+
{
|
| 371 |
+
"name": "MESSAGE",
|
| 372 |
+
"type": "string"
|
| 373 |
+
},
|
| 374 |
+
{
|
| 375 |
+
"name": "SECS",
|
| 376 |
+
"type": "number"
|
| 377 |
+
}
|
| 378 |
+
],
|
| 379 |
+
"example_standalone": "say [Hello!] for (2) seconds",
|
| 380 |
+
"example_with_other_blocks": [
|
| 381 |
+
{
|
| 382 |
+
"script": "when green flag clicked\n say [Grr] for (3) seconds\n say [Have you seen my honey? v] for (3) seconds\nend",
|
| 383 |
+
"explanation": "This script makes the sprite display two sequential speech bubbles with different messages and durations. First, it says 'Grr' for 3 seconds, then 'Have you seen my honey?' for another 3 seconds."
|
| 384 |
+
}
|
| 385 |
+
]
|
| 386 |
+
},
|
| 387 |
+
{
|
| 388 |
+
"block_name": "say ()",
|
| 389 |
+
"block_type": "Looks",
|
| 390 |
+
"block_shape": "Stack Block",
|
| 391 |
+
"op_code": "looks_say",
|
| 392 |
+
"functionality": "Displays a speech bubble with the specified text indefinitely until another 'say' or 'think' block is activated.",
|
| 393 |
+
"inputs": [
|
| 394 |
+
{
|
| 395 |
+
"name": "MESSAGE",
|
| 396 |
+
"type": "string"
|
| 397 |
+
}
|
| 398 |
+
],
|
| 399 |
+
"example_standalone": "say [Hello! v]",
|
| 400 |
+
"example_with_other_blocks": [
|
| 401 |
+
{
|
| 402 |
+
"script": "when green flag clicked\n say [Welcome to my game! v]\n wait (2) seconds\n say [] \nend",
|
| 403 |
+
"explanation": "This script makes the sprite say 'Welcome to my game!' for 2 seconds, then clears the speech bubble."
|
| 404 |
+
}
|
| 405 |
+
]
|
| 406 |
+
},
|
| 407 |
+
{
|
| 408 |
+
"block_name": "think () for () seconds",
|
| 409 |
+
"block_type": "Looks",
|
| 410 |
+
"block_shape": "Stack Block",
|
| 411 |
+
"op_code": "looks_thinkforsecs",
|
| 412 |
+
"functionality": "Displays a thought bubble containing specified text for a set duration.",
|
| 413 |
+
"inputs": [
|
| 414 |
+
{
|
| 415 |
+
"name": "MESSAGE",
|
| 416 |
+
"type": "string"
|
| 417 |
+
},
|
| 418 |
+
{
|
| 419 |
+
"name": "SECS",
|
| 420 |
+
"type": "number"
|
| 421 |
+
}
|
| 422 |
+
],
|
| 423 |
+
"example_standalone": "think [Hmm... v] for (2) seconds",
|
| 424 |
+
"example_with_other_blocks": [
|
| 425 |
+
{
|
| 426 |
+
"script": "when this sprite clicked\n think [What should I do? v] for (2) seconds\nend",
|
| 427 |
+
"explanation": "This script makes the sprite display a thought bubble saying 'What should I do?' for 2 seconds when clicked."
|
| 428 |
+
}
|
| 429 |
+
]
|
| 430 |
+
},
|
| 431 |
+
{
|
| 432 |
+
"block_name": "think ()",
|
| 433 |
+
"block_type": "Looks",
|
| 434 |
+
"block_shape": "Stack Block",
|
| 435 |
+
"op_code": "looks_think",
|
| 436 |
+
"functionality": "Displays a thought bubble with the specified text indefinitely until another 'say' or 'think' block is activated.",
|
| 437 |
+
"inputs": [
|
| 438 |
+
{
|
| 439 |
+
"name": "MESSAGE",
|
| 440 |
+
"type": "string"
|
| 441 |
+
}
|
| 442 |
+
],
|
| 443 |
+
"example_standalone": "think [Got it! v]",
|
| 444 |
+
"example_with_other_blocks": [
|
| 445 |
+
{
|
| 446 |
+
"script": "when I receive [correct answer v]\n think [That's right! v]\n wait (1) seconds\n think [good v] \nend",
|
| 447 |
+
"explanation": "This script makes the sprite think 'That's right!' for 1 second when a 'correct answer' broadcast is received, then clears the thought bubble."
|
| 448 |
+
}
|
| 449 |
+
]
|
| 450 |
+
},
|
| 451 |
+
{
|
| 452 |
+
"block_name": "switch costume to ()",
|
| 453 |
+
"block_type": "Looks",
|
| 454 |
+
"block_shape": "Stack Block",
|
| 455 |
+
"op_code": "looks_switchcostumeto",
|
| 456 |
+
"functionality": "Alters the sprite's appearance to a designated costume.",
|
| 457 |
+
"inputs": [
|
| 458 |
+
{
|
| 459 |
+
"name": "COSTUME",
|
| 460 |
+
"type": "dropdown/number"
|
| 461 |
+
}
|
| 462 |
+
],
|
| 463 |
+
"example_standalone": "switch costume to [costume1 v]",
|
| 464 |
+
"example_with_other_blocks": [
|
| 465 |
+
{
|
| 466 |
+
"script": "when I receive [explosion v]\n repeat (5)\n next costume\n end\n hide[costume1 v] \nend",
|
| 467 |
+
"explanation": "This script animates an explosion by rapidly switching costumes, then hides the sprite. [3]"
|
| 468 |
+
}
|
| 469 |
+
]
|
| 470 |
+
},
|
| 471 |
+
{
|
| 472 |
+
"block_name": "next costume",
|
| 473 |
+
"block_type": "Looks",
|
| 474 |
+
"block_shape": "Stack Block",
|
| 475 |
+
"op_code": "looks_nextcostume",
|
| 476 |
+
"functionality": "Switches the sprite's costume to the next one in its costume list. If it's the last costume, it cycles back to the first.",
|
| 477 |
+
"inputs": null,
|
| 478 |
+
"example_standalone": "next costume",
|
| 479 |
+
"example_with_other_blocks": [
|
| 480 |
+
{
|
| 481 |
+
"script": "when [space v] key pressed\n repeat (3)\n next costume\n wait (0.1) seconds\n end \nend",
|
| 482 |
+
"explanation": "When the space key is pressed, the sprite will cycle through its next three costumes with a short delay between each change."
|
| 483 |
+
},
|
| 484 |
+
{
|
| 485 |
+
"script": "when green flag clicked\n forever\n next costume\n wait (0.2) seconds\n end \nend",
|
| 486 |
+
"explanation": "This script continuously animates the sprite by switching to the next costume every 0.2 seconds, creating a walking or flying effect."
|
| 487 |
+
}
|
| 488 |
+
]
|
| 489 |
+
},
|
| 490 |
+
{
|
| 491 |
+
"block_name": "switch backdrop to ()",
|
| 492 |
+
"block_type": "Looks",
|
| 493 |
+
"block_shape": "Stack Block",
|
| 494 |
+
"op_code": "looks_switchbackdropto",
|
| 495 |
+
"functionality": "Changes the stage's backdrop to a specified backdrop.",
|
| 496 |
+
"inputs": [
|
| 497 |
+
{
|
| 498 |
+
"name": "BACKDROP",
|
| 499 |
+
"type": "dropdown/number"
|
| 500 |
+
}
|
| 501 |
+
],
|
| 502 |
+
"example_standalone": "switch backdrop to [backdrop1 v]",
|
| 503 |
+
"example_with_other_blocks": [
|
| 504 |
+
{
|
| 505 |
+
"script": "when green flag clicked\n switch backdrop to [start screen v]\nend ",
|
| 506 |
+
"explanation": "This script sets the stage to a 'start screen' backdrop when the project begins."
|
| 507 |
+
}
|
| 508 |
+
]
|
| 509 |
+
},
|
| 510 |
+
{
|
| 511 |
+
"block_name": "switch backdrop to () and wait",
|
| 512 |
+
"block_type": "Looks",
|
| 513 |
+
"block_shape": "Stack Block",
|
| 514 |
+
"op_code": "looks_switchbackdroptowait",
|
| 515 |
+
"functionality": "Changes the stage's backdrop to a specified backdrop and pauses the script until any 'When backdrop switches to' scripts for that backdrop have finished.",
|
| 516 |
+
"inputs": [
|
| 517 |
+
{
|
| 518 |
+
"name": "BACKDROP",
|
| 519 |
+
"type": "dropdown/number"
|
| 520 |
+
}
|
| 521 |
+
],
|
| 522 |
+
"example_standalone": "switch backdrop to [game over v] and wait",
|
| 523 |
+
"example_with_other_blocks": [
|
| 524 |
+
{
|
| 525 |
+
"script": "broadcast [game over v]\n switch backdrop to [game over v] and wait\n stop [all v] \nend",
|
| 526 |
+
"explanation": "This script broadcasts a 'game over' message, then changes the backdrop to 'game over' and waits for any associated scripts to finish before stopping all processes."
|
| 527 |
+
}
|
| 528 |
+
]
|
| 529 |
+
},
|
| 530 |
+
{
|
| 531 |
+
"block_name": "next backdrop",
|
| 532 |
+
"block_type": "Looks",
|
| 533 |
+
"block_shape": "Stack Block",
|
| 534 |
+
"op_code": "looks_nextbackdrop",
|
| 535 |
+
"functionality": "Switches the stage's backdrop to the next one in its backdrop list. If it's the last backdrop, it cycles back to the first.",
|
| 536 |
+
"inputs": null,
|
| 537 |
+
"example_standalone": "next backdrop",
|
| 538 |
+
"example_with_other_blocks": [
|
| 539 |
+
{
|
| 540 |
+
"script": "when [space v] key pressed\n next backdrop\nend",
|
| 541 |
+
"explanation": "This script changes the stage to the next backdrop in the list each time the space key is pressed."
|
| 542 |
+
}
|
| 543 |
+
]
|
| 544 |
+
},
|
| 545 |
+
{
|
| 546 |
+
"block_name": "change size by ()",
|
| 547 |
+
"block_type": "Looks",
|
| 548 |
+
"block_shape": "Stack Block",
|
| 549 |
+
"op_code": "looks_changesizeby",
|
| 550 |
+
"functionality": "Changes the sprite's size by a specified percentage. Positive values make it larger, negative values make it smaller.",
|
| 551 |
+
"inputs": [
|
| 552 |
+
{
|
| 553 |
+
"name": "CHANGE",
|
| 554 |
+
"type": "number"
|
| 555 |
+
}
|
| 556 |
+
],
|
| 557 |
+
"example_standalone": "change size by (10)",
|
| 558 |
+
"example_with_other_blocks": [
|
| 559 |
+
{
|
| 560 |
+
"script": "when green flag clicked\n repeat (10)\n change size by (5)\n wait (0.1) seconds\n end \nend ",
|
| 561 |
+
"explanation": "This script makes the sprite gradually grow larger over 10 steps, with a short pause between each size change."
|
| 562 |
+
}
|
| 563 |
+
]
|
| 564 |
+
},
|
| 565 |
+
{
|
| 566 |
+
"block_name": "set size to ()",
|
| 567 |
+
"block_type": "Looks",
|
| 568 |
+
"block_shape": "Stack Block",
|
| 569 |
+
"op_code": "looks_setsizeto",
|
| 570 |
+
"functionality": "Sets the sprite's size to a specific percentage of its original size.",
|
| 571 |
+
"inputs": [
|
| 572 |
+
{
|
| 573 |
+
"name": "SIZE",
|
| 574 |
+
"type": "number"
|
| 575 |
+
}
|
| 576 |
+
],
|
| 577 |
+
"example_standalone": "set size to (100)",
|
| 578 |
+
"example_with_other_blocks": [
|
| 579 |
+
{
|
| 580 |
+
"script": "when green flag clicked\n set size to (50)\n wait (1) seconds\n set size to (100) \nend ",
|
| 581 |
+
"explanation": "This script makes the sprite shrink to half its original size at the start, waits for 1 second, then returns to its original size."
|
| 582 |
+
}
|
| 583 |
+
]
|
| 584 |
+
},
|
| 585 |
+
{
|
| 586 |
+
"block_name": "change () effect by ()",
|
| 587 |
+
"block_type": "Looks",
|
| 588 |
+
"block_shape": "Stack Block",
|
| 589 |
+
"op_code": "looks_changeeffectby",
|
| 590 |
+
"functionality": "Changes a visual effect on the sprite by a specified amount (e.g., color, fisheye, whirl, pixelate, mosaic, brightness, ghost).",
|
| 591 |
+
"inputs": [
|
| 592 |
+
{
|
| 593 |
+
"name": "EFFECT",
|
| 594 |
+
"type": "dropdown",
|
| 595 |
+
"options": [
|
| 596 |
+
"color",
|
| 597 |
+
"fisheye",
|
| 598 |
+
"whirl",
|
| 599 |
+
"pixelate",
|
| 600 |
+
"mosaic",
|
| 601 |
+
"brightness",
|
| 602 |
+
"ghost"
|
| 603 |
+
]
|
| 604 |
+
},
|
| 605 |
+
{
|
| 606 |
+
"name": "CHANGE",
|
| 607 |
+
"type": "number"
|
| 608 |
+
}
|
| 609 |
+
],
|
| 610 |
+
"example_standalone": "change [color v] effect by (25)",
|
| 611 |
+
"example_with_other_blocks": [
|
| 612 |
+
{
|
| 613 |
+
"script": "when loudness > (10)\n change [fisheye v] effect by (5)\nend",
|
| 614 |
+
"explanation": "When the loudness detected by the microphone is greater than 10, the sprite's fisheye effect will increase by 5."
|
| 615 |
+
},
|
| 616 |
+
{
|
| 617 |
+
"script": "when green flag clicked\n forever\n change [color v] effect by (5)\n wait (0.1) seconds\n end \nend",
|
| 618 |
+
"explanation": "This script makes the sprite continuously cycle through different colors."
|
| 619 |
+
}
|
| 620 |
+
]
|
| 621 |
+
},
|
| 622 |
+
{
|
| 623 |
+
"block_name": "set () effect to ()",
|
| 624 |
+
"block_type": "Looks",
|
| 625 |
+
"block_shape": "Stack Block",
|
| 626 |
+
"op_code": "looks_seteffectto",
|
| 627 |
+
"functionality": "Sets a visual effect on the sprite to a specific value.",
|
| 628 |
+
"inputs": [
|
| 629 |
+
{
|
| 630 |
+
"name": "EFFECT",
|
| 631 |
+
"type": "dropdown",
|
| 632 |
+
"options": [
|
| 633 |
+
"color",
|
| 634 |
+
"fisheye",
|
| 635 |
+
"whirl",
|
| 636 |
+
"pixelate",
|
| 637 |
+
"mosaic",
|
| 638 |
+
"brightness",
|
| 639 |
+
"ghost"
|
| 640 |
+
]
|
| 641 |
+
},
|
| 642 |
+
{
|
| 643 |
+
"name": "VALUE",
|
| 644 |
+
"type": "number"
|
| 645 |
+
}
|
| 646 |
+
],
|
| 647 |
+
"example_standalone": "set [ghost v] effect to (50)",
|
| 648 |
+
"example_with_other_blocks": [
|
| 649 |
+
{
|
| 650 |
+
"script": "when green flag clicked\n set [ghost v] effect to (75)\nend",
|
| 651 |
+
"explanation": "This script makes the sprite 75% transparent at the start of the project."
|
| 652 |
+
}
|
| 653 |
+
]
|
| 654 |
+
},
|
| 655 |
+
{
|
| 656 |
+
"block_name": "clear graphic effects",
|
| 657 |
+
"block_type": "Looks",
|
| 658 |
+
"block_shape": "Stack Block",
|
| 659 |
+
"op_code": "looks_cleargraphiceffects",
|
| 660 |
+
"functionality": "Removes all visual effects applied to the sprite.",
|
| 661 |
+
"inputs": null,
|
| 662 |
+
"example_standalone": "clear graphic effects",
|
| 663 |
+
"example_with_other_blocks": [
|
| 664 |
+
{
|
| 665 |
+
"script": "when green flag clicked\n change [color v] effect by (50)\n wait (2) seconds\n clear graphic effects\nend",
|
| 666 |
+
"explanation": "This script changes the sprite's color effect, waits 2 seconds, then resets all graphic effects."
|
| 667 |
+
}
|
| 668 |
+
]
|
| 669 |
+
},
|
| 670 |
+
{
|
| 671 |
+
"block_name": "show",
|
| 672 |
+
"block_type": "Looks",
|
| 673 |
+
"block_shape": "Stack Block",
|
| 674 |
+
"op_code": "looks_show",
|
| 675 |
+
"functionality": "Makes the sprite visible on the stage.",
|
| 676 |
+
"inputs": null,
|
| 677 |
+
"example_standalone": "show",
|
| 678 |
+
"example_with_other_blocks": [
|
| 679 |
+
{
|
| 680 |
+
"script": "when green flag clicked\n hide[start game v]\nwhen I receive [start game v]\n show [start game v] \nend",
|
| 681 |
+
"explanation": "This script hides the sprite at the beginning of the project and makes it visible when a 'start game' broadcast is received."
|
| 682 |
+
}
|
| 683 |
+
]
|
| 684 |
+
},
|
| 685 |
+
{
|
| 686 |
+
"block_name": "hide",
|
| 687 |
+
"block_type": "Looks",
|
| 688 |
+
"block_shape": "Stack Block",
|
| 689 |
+
"op_code": "looks_hide",
|
| 690 |
+
"functionality": "Makes the sprite invisible on the stage.",
|
| 691 |
+
"inputs": null,
|
| 692 |
+
"example_standalone": "hide",
|
| 693 |
+
"example_with_other_blocks": [
|
| 694 |
+
{
|
| 695 |
+
"script": "when green flag clicked\n hide \nend",
|
| 696 |
+
"explanation": "This script hides the sprite from the stage when the green flag is clicked."
|
| 697 |
+
}
|
| 698 |
+
]
|
| 699 |
+
},
|
| 700 |
+
{
|
| 701 |
+
"block_name": "go to () layer",
|
| 702 |
+
"block_type": "Looks",
|
| 703 |
+
"block_shape": "Stack Block",
|
| 704 |
+
"op_code": "looks_gotofrontback",
|
| 705 |
+
"functionality": "Moves the sprite to the front-most or back-most layer of other sprites on the stage.",
|
| 706 |
+
"inputs": [
|
| 707 |
+
{
|
| 708 |
+
"name": "FRONT_BACK",
|
| 709 |
+
"type": "dropdown",
|
| 710 |
+
"options": [
|
| 711 |
+
"front",
|
| 712 |
+
"back"
|
| 713 |
+
]
|
| 714 |
+
}
|
| 715 |
+
],
|
| 716 |
+
"example_standalone": "go to [front v] layer",
|
| 717 |
+
"example_with_other_blocks": [
|
| 718 |
+
{
|
| 719 |
+
"script": "when green flag clicked\n go to [front v] layer\nend",
|
| 720 |
+
"explanation": "This script ensures the sprite is always visible on top of other sprites at the start of the project."
|
| 721 |
+
}
|
| 722 |
+
]
|
| 723 |
+
},
|
| 724 |
+
{
|
| 725 |
+
"block_name": "go () layers",
|
| 726 |
+
"block_type": "Looks",
|
| 727 |
+
"block_shape": "Stack Block",
|
| 728 |
+
"op_code": "looks_goforwardbackwardlayers",
|
| 729 |
+
"functionality": "Moves the sprite forward or backward a specified number of layers in relation to other sprites.",
|
| 730 |
+
"inputs": [
|
| 731 |
+
{
|
| 732 |
+
"name": "FORWARD_BACKWARD",
|
| 733 |
+
"type": "dropdown",
|
| 734 |
+
"options": [
|
| 735 |
+
"forward",
|
| 736 |
+
"backward"
|
| 737 |
+
]
|
| 738 |
+
},
|
| 739 |
+
{
|
| 740 |
+
"name": "NUM",
|
| 741 |
+
"type": "number"
|
| 742 |
+
}
|
| 743 |
+
],
|
| 744 |
+
"example_standalone": "go [forward v] (1) layers",
|
| 745 |
+
"example_with_other_blocks": [
|
| 746 |
+
{
|
| 747 |
+
"script": "when this sprite clicked go [forward v] (1) layers\nend",
|
| 748 |
+
"explanation": "This script brings the clicked sprite one layer closer to the front."
|
| 749 |
+
}
|
| 750 |
+
]
|
| 751 |
+
},
|
| 752 |
+
{
|
| 753 |
+
"block_name": "play sound () until done",
|
| 754 |
+
"block_type": "Sound",
|
| 755 |
+
"block_shape": "Stack Block",
|
| 756 |
+
"op_code": "sound_playuntildone",
|
| 757 |
+
"functionality": "Plays a specified sound and pauses the script's execution until the sound has completed.",
|
| 758 |
+
"inputs": [
|
| 759 |
+
{
|
| 760 |
+
"name": "sound name",
|
| 761 |
+
"type": "dropdown"
|
| 762 |
+
}
|
| 763 |
+
],
|
| 764 |
+
"example_standalone": "play sound [Meow v] until done",
|
| 765 |
+
"example_with_other_blocks": [
|
| 766 |
+
{
|
| 767 |
+
"script": "when backdrop switches to [winning screen v]\n play sound [fanfare v] until done\n say [You won!] for (2) seconds\nend",
|
| 768 |
+
"explanation": "When the backdrop changes to the 'winning screen', a 'fanfare' sound will play until it finishes, and then the sprite will say 'You won!' for 2 seconds."
|
| 769 |
+
},
|
| 770 |
+
{
|
| 771 |
+
"script": "forever\n play sound [Music v] until done \nend",
|
| 772 |
+
"explanation": "This script creates a continuous loop for background music, playing the 'Music' sound repeatedly."
|
| 773 |
+
}
|
| 774 |
+
]
|
| 775 |
+
},
|
| 776 |
+
{
|
| 777 |
+
"block_name": "start sound ()",
|
| 778 |
+
"block_type": "Sound",
|
| 779 |
+
"block_shape": "Stack Block",
|
| 780 |
+
"op_code": "sound_play",
|
| 781 |
+
"functionality": "Initiates playback of a specified sound without pausing the script, allowing other actions to proceed concurrently.",
|
| 782 |
+
"inputs": [
|
| 783 |
+
{
|
| 784 |
+
"name": "sound name",
|
| 785 |
+
"type": "dropdown"
|
| 786 |
+
}
|
| 787 |
+
],
|
| 788 |
+
"example_standalone": "start sound [Pop v]",
|
| 789 |
+
"example_with_other_blocks": [
|
| 790 |
+
{
|
| 791 |
+
"script": "when this sprite clicked\n start sound [Pop v]\n change [score v] by (1)\nend",
|
| 792 |
+
"explanation": "This script plays a 'Pop' sound and increments the score simultaneously when the sprite is clicked."
|
| 793 |
+
}
|
| 794 |
+
]
|
| 795 |
+
},
|
| 796 |
+
{
|
| 797 |
+
"block_name": "stop all sounds",
|
| 798 |
+
"block_type": "Sound",
|
| 799 |
+
"block_shape": "Stack Block",
|
| 800 |
+
"op_code": "sound_stopallsounds",
|
| 801 |
+
"functionality": "Stops all currently playing sounds.",
|
| 802 |
+
"inputs": null,
|
| 803 |
+
"example_standalone": "stop all sounds",
|
| 804 |
+
"example_with_other_blocks": [
|
| 805 |
+
{
|
| 806 |
+
"script": "when I receive [game over v]\n stop all sounds\nend",
|
| 807 |
+
"explanation": "This script stops any sounds currently playing when the 'game over' broadcast is received."
|
| 808 |
+
}
|
| 809 |
+
]
|
| 810 |
+
},
|
| 811 |
+
{
|
| 812 |
+
"block_name": "change volume by ()",
|
| 813 |
+
"block_type": "Sound",
|
| 814 |
+
"block_shape": "Stack Block",
|
| 815 |
+
"op_code": "sound_changevolumeby",
|
| 816 |
+
"functionality": "Changes the project's sound volume by a specified amount.",
|
| 817 |
+
"inputs": [
|
| 818 |
+
{
|
| 819 |
+
"name": "change",
|
| 820 |
+
"type": "number"
|
| 821 |
+
}
|
| 822 |
+
],
|
| 823 |
+
"example_standalone": "change volume by (-10)",
|
| 824 |
+
"example_with_other_blocks": [
|
| 825 |
+
{
|
| 826 |
+
"script": "when [down arrow v] key pressed\n change volume by (-5)\nend",
|
| 827 |
+
"explanation": "This script decreases the project's volume by 5 when the down arrow key is pressed."
|
| 828 |
+
}
|
| 829 |
+
]
|
| 830 |
+
},
|
| 831 |
+
{
|
| 832 |
+
"block_name": "set volume to () %",
|
| 833 |
+
"block_type": "Sound",
|
| 834 |
+
"block_shape": "Stack Block",
|
| 835 |
+
"op_code": "sound_setvolumeto",
|
| 836 |
+
"functionality": "Sets the sound volume to a specific percentage (0-100).",
|
| 837 |
+
"inputs": [
|
| 838 |
+
{
|
| 839 |
+
"name": "percentage",
|
| 840 |
+
"type": "number"
|
| 841 |
+
}
|
| 842 |
+
],
|
| 843 |
+
"example_standalone": "set volume to (100) %",
|
| 844 |
+
"example_with_other_blocks": [
|
| 845 |
+
{
|
| 846 |
+
"script": "when green flag clicked\n set volume to (50) %\nend",
|
| 847 |
+
"explanation": "This script sets the project's volume to 50% when the green flag is clicked."
|
| 848 |
+
}
|
| 849 |
+
]
|
| 850 |
+
},
|
| 851 |
+
{
|
| 852 |
+
"block_name": "broadcast ()",
|
| 853 |
+
"block_type": "Events",
|
| 854 |
+
"block_shape": "Stack Block",
|
| 855 |
+
"op_code": "event_broadcast",
|
| 856 |
+
"functionality": "Sends a broadcast message throughout the Scratch program, activating any 'when I receive ()' blocks that are set to listen for that message, enabling indirect communication.",
|
| 857 |
+
"inputs": [
|
| 858 |
+
{
|
| 859 |
+
"name": "message name",
|
| 860 |
+
"type": "string/dropdown"
|
| 861 |
+
}
|
| 862 |
+
],
|
| 863 |
+
"example_standalone": "broadcast [start game v]",
|
| 864 |
+
"example_with_other_blocks": [
|
| 865 |
+
{
|
| 866 |
+
"script": "if <key [space v] pressed?> then\n broadcast [jump v]\nend",
|
| 867 |
+
"explanation": "This script sends a 'jump' message to other scripts or sprites when the space key is pressed."
|
| 868 |
+
}
|
| 869 |
+
]
|
| 870 |
+
},
|
| 871 |
+
{
|
| 872 |
+
"block_name": "broadcast () and wait",
|
| 873 |
+
"block_type": "Events",
|
| 874 |
+
"block_shape": "Stack Block",
|
| 875 |
+
"op_code": "event_broadcastandwait",
|
| 876 |
+
"functionality": "Sends a broadcast message and pauses the current script until all other scripts activated by that broadcast have completed their execution, ensuring sequential coordination.",
|
| 877 |
+
"inputs": [
|
| 878 |
+
{
|
| 879 |
+
"name": "message name",
|
| 880 |
+
"type": "string/dropdown"
|
| 881 |
+
}
|
| 882 |
+
],
|
| 883 |
+
"example_standalone": "broadcast [initialize sprites v] and wait",
|
| 884 |
+
"example_with_other_blocks": [
|
| 885 |
+
{
|
| 886 |
+
"script": "broadcast [initialize sprites v] and wait\n say [Game Started!] for (2) seconds",
|
| 887 |
+
"explanation": "This script ensures all sprite initialization routines complete before displaying 'Game Started!' for 2 seconds."
|
| 888 |
+
}
|
| 889 |
+
]
|
| 890 |
+
},
|
| 891 |
+
{
|
| 892 |
+
"block_name": "wait () seconds",
|
| 893 |
+
"block_type": "Control",
|
| 894 |
+
"block_shape": "Stack Block",
|
| 895 |
+
"op_code": "control_wait",
|
| 896 |
+
"functionality": "Pauses the script for a specified duration.",
|
| 897 |
+
"inputs": [
|
| 898 |
+
{
|
| 899 |
+
"name": "seconds",
|
| 900 |
+
"type": "number"
|
| 901 |
+
}
|
| 902 |
+
],
|
| 903 |
+
"example_standalone": "wait (1) seconds",
|
| 904 |
+
"example_with_other_blocks": [
|
| 905 |
+
{
|
| 906 |
+
"script": "say [Hello!] for (1) seconds\n wait (0.5) seconds\n say [Goodbye!] for (1) seconds",
|
| 907 |
+
"explanation": "This script creates a timed dialogue sequence, pausing for 0.5 seconds between two speech bubbles."
|
| 908 |
+
}
|
| 909 |
+
]
|
| 910 |
+
},
|
| 911 |
+
{
|
| 912 |
+
"block_name": "wait until <>",
|
| 913 |
+
"block_type": "Control",
|
| 914 |
+
"block_shape": "Stack Block",
|
| 915 |
+
"op_code": "control_wait_until",
|
| 916 |
+
"functionality": "Pauses the script until the specified boolean condition becomes true. [NOTE: it takes boolean blocks as input]",
|
| 917 |
+
"inputs": [
|
| 918 |
+
{
|
| 919 |
+
"name": "condition",
|
| 920 |
+
"type": "boolean"
|
| 921 |
+
}
|
| 922 |
+
],
|
| 923 |
+
"example_standalone": "wait until <key [space v] pressed?>",
|
| 924 |
+
"example_with_other_blocks": [
|
| 925 |
+
{
|
| 926 |
+
"script": "wait until <key [space v] pressed?>\n start sound [pop v]\nend",
|
| 927 |
+
"explanation": "This script pauses until the space key is pressed, then plays a 'pop' sound."
|
| 928 |
+
}
|
| 929 |
+
]
|
| 930 |
+
},
|
| 931 |
+
{
|
| 932 |
+
"block_name": "stop ()",
|
| 933 |
+
"block_type": "Control",
|
| 934 |
+
"block_shape": "Stack Block",
|
| 935 |
+
"op_code": "control_stop",
|
| 936 |
+
"functionality": "Stops all scripts, this script, or other scripts in the sprite. Becomes a Cap Block if 'all' or 'this script' is selected in the dropdown menu.",
|
| 937 |
+
"inputs": [
|
| 938 |
+
{
|
| 939 |
+
"name": "option",
|
| 940 |
+
"type": "dropdown",
|
| 941 |
+
"options": [
|
| 942 |
+
"all",
|
| 943 |
+
"this script",
|
| 944 |
+
"other scripts in sprite"
|
| 945 |
+
]
|
| 946 |
+
}
|
| 947 |
+
],
|
| 948 |
+
"example_standalone": "stop [all v]",
|
| 949 |
+
"example_with_other_blocks": [
|
| 950 |
+
{
|
| 951 |
+
"script": "if <score = (0)> then\n stop [all v]\nend",
|
| 952 |
+
"explanation": "This script stops the entire project if the 'score' variable becomes 0."
|
| 953 |
+
}
|
| 954 |
+
]
|
| 955 |
+
},
|
| 956 |
+
{
|
| 957 |
+
"block_name": "create clone of ()",
|
| 958 |
+
"block_type": "Control",
|
| 959 |
+
"block_shape": "Stack Block",
|
| 960 |
+
"op_code": "control_create_clone_of",
|
| 961 |
+
"functionality": "Generates a copy, or clone, of a specified sprite (or 'myself' for the current sprite).",
|
| 962 |
+
"inputs": [
|
| 963 |
+
{
|
| 964 |
+
"name": "sprite_name",
|
| 965 |
+
"type": "dropdown",
|
| 966 |
+
"options": [
|
| 967 |
+
"myself",
|
| 968 |
+
"sprite1",
|
| 969 |
+
"..."
|
| 970 |
+
]
|
| 971 |
+
}
|
| 972 |
+
],
|
| 973 |
+
"example_standalone": "create clone of [myself v]",
|
| 974 |
+
"example_with_other_blocks": [
|
| 975 |
+
{
|
| 976 |
+
"script": "when I start as a clone\n show\n go to random position\n wait (2) seconds\n delete this clone\nend",
|
| 977 |
+
"explanation": "When a clone is created, it will show itself, go to a random position, wait for 2 seconds, and then delete itself."
|
| 978 |
+
}
|
| 979 |
+
]
|
| 980 |
+
},
|
| 981 |
+
{
|
| 982 |
+
"block_name": "delete this clone",
|
| 983 |
+
"block_type": "Control",
|
| 984 |
+
"block_shape": "Stack Block",
|
| 985 |
+
"op_code": "control_delete_this_clone",
|
| 986 |
+
"functionality": "Deletes the clone that is currently running the script.",
|
| 987 |
+
"inputs": null,
|
| 988 |
+
"example_standalone": "delete this clone",
|
| 989 |
+
"example_with_other_blocks": [
|
| 990 |
+
{
|
| 991 |
+
"script": "when I start as a clone\n wait (5) seconds\n delete this clone\nend",
|
| 992 |
+
"explanation": "This script makes each clone wait for 5 seconds after it's created, then deletes itself."
|
| 993 |
+
}
|
| 994 |
+
]
|
| 995 |
+
},
|
| 996 |
+
{
|
| 997 |
+
"block_name": "set [my variable v] to ()",
|
| 998 |
+
"block_type": "Data",
|
| 999 |
+
"block_shape": "Stack Block",
|
| 1000 |
+
"op_code": "data_setvariableto",
|
| 1001 |
+
"functionality": "Assigns a specific value (number, string, or boolean) to a variable.",
|
| 1002 |
+
"inputs": [
|
| 1003 |
+
{
|
| 1004 |
+
"name": "variable name",
|
| 1005 |
+
"type": "dropdown"
|
| 1006 |
+
},
|
| 1007 |
+
{
|
| 1008 |
+
"name": "value",
|
| 1009 |
+
"type": "any"
|
| 1010 |
+
}
|
| 1011 |
+
],
|
| 1012 |
+
"example_standalone": "set [score v] to (0)",
|
| 1013 |
+
"example_with_other_blocks": [
|
| 1014 |
+
{
|
| 1015 |
+
"script": "when green flag clicked\n set [score v] to (0)\n set [player name v] to [Guest]\nend",
|
| 1016 |
+
"explanation": "This script initializes the 'score' variable to 0 and the 'player name' variable to 'Guest' when the project starts."
|
| 1017 |
+
}
|
| 1018 |
+
]
|
| 1019 |
+
},
|
| 1020 |
+
{
|
| 1021 |
+
"block_name": "change [my variable v] by ()",
|
| 1022 |
+
"block_type": "Data",
|
| 1023 |
+
"block_shape": "Stack Block",
|
| 1024 |
+
"op_code": "data_changevariableby",
|
| 1025 |
+
"functionality": "Increases or decreases a variable's numerical value by a specified amount.",
|
| 1026 |
+
"inputs": [
|
| 1027 |
+
{
|
| 1028 |
+
"name": "variable name",
|
| 1029 |
+
"type": "dropdown"
|
| 1030 |
+
},
|
| 1031 |
+
{
|
| 1032 |
+
"name": "value",
|
| 1033 |
+
"type": "number"
|
| 1034 |
+
}
|
| 1035 |
+
],
|
| 1036 |
+
"example_standalone": "change [score v] by (1)",
|
| 1037 |
+
"example_with_other_blocks": [
|
| 1038 |
+
{
|
| 1039 |
+
"script": "when this sprite clicked\n change [score v] by (1)\nend",
|
| 1040 |
+
"explanation": "This script increments the 'score' variable by 1 each time the sprite is clicked."
|
| 1041 |
+
}
|
| 1042 |
+
]
|
| 1043 |
+
},
|
| 1044 |
+
{
|
| 1045 |
+
"block_name": "add () to [my list v]",
|
| 1046 |
+
"block_type": "Data",
|
| 1047 |
+
"block_shape": "Stack Block",
|
| 1048 |
+
"op_code": "data_addtolist",
|
| 1049 |
+
"functionality": "Appends an item to the end of a list.",
|
| 1050 |
+
"inputs": [
|
| 1051 |
+
{
|
| 1052 |
+
"name": "item",
|
| 1053 |
+
"type": "any"
|
| 1054 |
+
},
|
| 1055 |
+
{
|
| 1056 |
+
"name": "list name",
|
| 1057 |
+
"type": "dropdown"
|
| 1058 |
+
}
|
| 1059 |
+
],
|
| 1060 |
+
"example_standalone": "add [apple] to [shopping list v]",
|
| 1061 |
+
"example_with_other_blocks": [
|
| 1062 |
+
{
|
| 1063 |
+
"script": "when green flag clicked\n add [apple] to [shopping list v]\n add [banana] to [shopping list v]\nend",
|
| 1064 |
+
"explanation": "This script adds 'apple' and 'banana' as new items to the 'shopping list' when the project starts."
|
| 1065 |
+
}
|
| 1066 |
+
]
|
| 1067 |
+
},
|
| 1068 |
+
{
|
| 1069 |
+
"block_name": "delete () of [my list v]",
|
| 1070 |
+
"block_type": "Data",
|
| 1071 |
+
"block_shape": "Stack Block",
|
| 1072 |
+
"op_code": "data_deleteoflist",
|
| 1073 |
+
"functionality": "Removes an item from a list by its index or by selecting 'all' items.",
|
| 1074 |
+
"inputs": [
|
| 1075 |
+
{
|
| 1076 |
+
"name": "index/option",
|
| 1077 |
+
"type": "number/dropdown",
|
| 1078 |
+
"options": [
|
| 1079 |
+
"all",
|
| 1080 |
+
"last",
|
| 1081 |
+
"random"
|
| 1082 |
+
]
|
| 1083 |
+
},
|
| 1084 |
+
{
|
| 1085 |
+
"name": "list name",
|
| 1086 |
+
"type": "dropdown"
|
| 1087 |
+
}
|
| 1088 |
+
],
|
| 1089 |
+
"example_standalone": "delete (1) of [my list v]",
|
| 1090 |
+
"example_with_other_blocks": [
|
| 1091 |
+
{
|
| 1092 |
+
"script": "when green flag clicked\n delete (all) of [my list v]\nend",
|
| 1093 |
+
"explanation": "This script clears all items from 'my list' when the green flag is clicked."
|
| 1094 |
+
}
|
| 1095 |
+
]
|
| 1096 |
+
},
|
| 1097 |
+
{
|
| 1098 |
+
"block_name": "insert () at () of [my list v]",
|
| 1099 |
+
"block_type": "Data",
|
| 1100 |
+
"block_shape": "Stack Block",
|
| 1101 |
+
"op_code": "data_insertatlist",
|
| 1102 |
+
"functionality": "Inserts an item at a specific position within a list.",
|
| 1103 |
+
"inputs": [
|
| 1104 |
+
{
|
| 1105 |
+
"name": "item",
|
| 1106 |
+
"type": "any"
|
| 1107 |
+
},
|
| 1108 |
+
{
|
| 1109 |
+
"name": "index",
|
| 1110 |
+
"type": "number"
|
| 1111 |
+
},
|
| 1112 |
+
{
|
| 1113 |
+
"name": "list name",
|
| 1114 |
+
"type": "dropdown"
|
| 1115 |
+
}
|
| 1116 |
+
],
|
| 1117 |
+
"example_standalone": "insert [orange] at (2) of [fruits v]",
|
| 1118 |
+
"example_with_other_blocks": [
|
| 1119 |
+
{
|
| 1120 |
+
"script": "insert [orange] at (2) of [fruits v]",
|
| 1121 |
+
"explanation": "This script inserts 'orange' as the second item in the 'fruits' list, shifting subsequent items."
|
| 1122 |
+
}
|
| 1123 |
+
]
|
| 1124 |
+
},
|
| 1125 |
+
{
|
| 1126 |
+
"block_name": "replace item () of [my list v] with ()",
|
| 1127 |
+
"block_type": "Data",
|
| 1128 |
+
"block_shape": "Stack Block",
|
| 1129 |
+
"op_code": "data_replaceitemoflist",
|
| 1130 |
+
"functionality": "Replaces an item at a specific position in a list with a new value.",
|
| 1131 |
+
"inputs": [
|
| 1132 |
+
{
|
| 1133 |
+
"name": "index",
|
| 1134 |
+
"type": "number"
|
| 1135 |
+
},
|
| 1136 |
+
{
|
| 1137 |
+
"name": "list name",
|
| 1138 |
+
"type": "dropdown"
|
| 1139 |
+
},
|
| 1140 |
+
{
|
| 1141 |
+
"name": "new item",
|
| 1142 |
+
"type": "any"
|
| 1143 |
+
}
|
| 1144 |
+
],
|
| 1145 |
+
"example_standalone": "replace item (1) of [colors v] with [blue]",
|
| 1146 |
+
"example_with_other_blocks": [
|
| 1147 |
+
{
|
| 1148 |
+
"script": "replace item (1) of [colors v] with [blue]",
|
| 1149 |
+
"explanation": "This script changes the first item in the 'colors' list to 'blue'."
|
| 1150 |
+
}
|
| 1151 |
+
]
|
| 1152 |
+
},
|
| 1153 |
+
{
|
| 1154 |
+
"block_name": "show variable [my variable v]",
|
| 1155 |
+
"block_type": "Data",
|
| 1156 |
+
"block_shape": "Stack Block",
|
| 1157 |
+
"op_code": "data_showvariable",
|
| 1158 |
+
"functionality": "Makes a variable's monitor visible on the stage.",
|
| 1159 |
+
"inputs": [
|
| 1160 |
+
{
|
| 1161 |
+
"name": "variable name",
|
| 1162 |
+
"type": "dropdown"
|
| 1163 |
+
}
|
| 1164 |
+
],
|
| 1165 |
+
"example_standalone": "show variable [score v]",
|
| 1166 |
+
"example_with_other_blocks": [
|
| 1167 |
+
{
|
| 1168 |
+
"script": "when green flag clicked\n show variable [score v]\nend",
|
| 1169 |
+
"explanation": "This script displays the 'score' variable on the stage when the project starts."
|
| 1170 |
+
}
|
| 1171 |
+
]
|
| 1172 |
+
},
|
| 1173 |
+
{
|
| 1174 |
+
"block_name": "hide variable [my variable v]",
|
| 1175 |
+
"block_type": "Data",
|
| 1176 |
+
"block_shape": "Stack Block",
|
| 1177 |
+
"op_code": "data_hidevariable",
|
| 1178 |
+
"functionality": "Hides a variable's monitor from the stage.",
|
| 1179 |
+
"inputs": [
|
| 1180 |
+
{
|
| 1181 |
+
"name": "variable name",
|
| 1182 |
+
"type": "dropdown"
|
| 1183 |
+
}
|
| 1184 |
+
],
|
| 1185 |
+
"example_standalone": "hide variable [score v]",
|
| 1186 |
+
"example_with_other_blocks": [
|
| 1187 |
+
{
|
| 1188 |
+
"script": "when I receive [game over v]\n hide variable [score v]\nend",
|
| 1189 |
+
"explanation": "This script hides the 'score' variable when the 'game over' broadcast is received."
|
| 1190 |
+
}
|
| 1191 |
+
]
|
| 1192 |
+
},
|
| 1193 |
+
{
|
| 1194 |
+
"block_name": "show list [my list v]",
|
| 1195 |
+
"block_type": "Data",
|
| 1196 |
+
"block_shape": "Stack Block",
|
| 1197 |
+
"op_code": "data_showlist",
|
| 1198 |
+
"functionality": "Makes a list's monitor visible on the stage.",
|
| 1199 |
+
"inputs": [
|
| 1200 |
+
{
|
| 1201 |
+
"name": "list name",
|
| 1202 |
+
"type": "dropdown"
|
| 1203 |
+
}
|
| 1204 |
+
],
|
| 1205 |
+
"example_standalone": "show list [shopping list v]",
|
| 1206 |
+
"example_with_other_blocks": [
|
| 1207 |
+
{
|
| 1208 |
+
"script": "when green flag clicked\n show list [shopping list v]\nend",
|
| 1209 |
+
"explanation": "This script displays the 'shopping list' on the stage when the project starts."
|
| 1210 |
+
}
|
| 1211 |
+
]
|
| 1212 |
+
},
|
| 1213 |
+
{
|
| 1214 |
+
"block_name": "hide list [my list v]",
|
| 1215 |
+
"block_type": "Data",
|
| 1216 |
+
"block_shape": "Stack Block",
|
| 1217 |
+
"op_code": "data_hidelist",
|
| 1218 |
+
"functionality": "Hides a list's monitor from the stage.",
|
| 1219 |
+
"inputs": [
|
| 1220 |
+
{
|
| 1221 |
+
"name": "list name",
|
| 1222 |
+
"type": "dropdown"
|
| 1223 |
+
}
|
| 1224 |
+
],
|
| 1225 |
+
"example_standalone": "hide list [shopping list v]",
|
| 1226 |
+
"example_with_other_blocks": [
|
| 1227 |
+
{
|
| 1228 |
+
"script": "when I receive [game over v]\n hide list [shopping list v]\nend",
|
| 1229 |
+
"explanation": "This script hides the 'shopping list' when the 'game over' broadcast is received."
|
| 1230 |
+
}
|
| 1231 |
+
]
|
| 1232 |
+
},
|
| 1233 |
+
{
|
| 1234 |
+
"block_name": "Ask () and Wait",
|
| 1235 |
+
"block_type": "Sensing",
|
| 1236 |
+
"block_shape": "Stack Block",
|
| 1237 |
+
"op_code": "sensing_askandwait",
|
| 1238 |
+
"functionality": "Displays an input box with specified text at the bottom of the screen, allowing users to input text, which is stored in the 'Answer' block.",
|
| 1239 |
+
"inputs": [
|
| 1240 |
+
{
|
| 1241 |
+
"name": "question",
|
| 1242 |
+
"type": "text"
|
| 1243 |
+
}
|
| 1244 |
+
],
|
| 1245 |
+
"example_standalone": "ask [What is your name? v] and wait",
|
| 1246 |
+
"example_with_other_blocks": [
|
| 1247 |
+
{
|
| 1248 |
+
"script": "ask [What is your name? v] and wait\n say join [Hello v] (answer) for (2) seconds \nend",
|
| 1249 |
+
"explanation": "This script prompts the user for their name, waits for input, then greets them using the provided answer."
|
| 1250 |
+
}
|
| 1251 |
+
]
|
| 1252 |
+
},
|
| 1253 |
+
{
|
| 1254 |
+
"block_name": "Reset Timer",
|
| 1255 |
+
"block_type": "Sensing",
|
| 1256 |
+
"block_shape": "Stack Block",
|
| 1257 |
+
"op_code": "sensing_resettimer",
|
| 1258 |
+
"functionality": "Sets the timer’s value back to 0.0.",
|
| 1259 |
+
"inputs": null,
|
| 1260 |
+
"example_standalone": "reset timer",
|
| 1261 |
+
"example_with_other_blocks": [
|
| 1262 |
+
{
|
| 1263 |
+
"script": "when green flag clicked\n reset timer\n wait (5) seconds\n say timer for (2) seconds\nend",
|
| 1264 |
+
"explanation": "This script resets the timer at the start, waits for 5 seconds, then says the current timer value."
|
| 1265 |
+
}
|
| 1266 |
+
]
|
| 1267 |
+
},
|
| 1268 |
+
{
|
| 1269 |
+
"block_name": "set drag mode [draggable v]",
|
| 1270 |
+
"block_type": "Sensing",
|
| 1271 |
+
"block_shape": "Stack Block",
|
| 1272 |
+
"op_code": "sensing_setdragmode",
|
| 1273 |
+
"functionality": "Sets whether the sprite can be dragged by the mouse on the stage.",
|
| 1274 |
+
"inputs": null,
|
| 1275 |
+
"fields": {
|
| 1276 |
+
"DRAG_MODE": {
|
| 1277 |
+
"type": "dropdown",
|
| 1278 |
+
"options": [
|
| 1279 |
+
"draggable",
|
| 1280 |
+
"not draggable"
|
| 1281 |
+
]
|
| 1282 |
+
}
|
| 1283 |
+
},
|
| 1284 |
+
"example_standalone": "set drag mode [draggable v]",
|
| 1285 |
+
"example_with_other_blocks": [
|
| 1286 |
+
{
|
| 1287 |
+
"script": "when green flag clicked\n set drag mode [not draggable v]\nend when this sprite clicked\n set drag mode [draggable v] \nend",
|
| 1288 |
+
"explanation": "This script makes the sprite not draggable when the project starts, but allows it to be dragged once it's clicked."
|
| 1289 |
+
}
|
| 1290 |
+
]
|
| 1291 |
+
},
|
| 1292 |
+
{
|
| 1293 |
+
"block_name": "[my custom block]",
|
| 1294 |
+
"block_type": "My Blocks",
|
| 1295 |
+
"block_shape": "Stack Block",
|
| 1296 |
+
"op_code": "procedures_call",
|
| 1297 |
+
"functionality": "Executes the script defined by a corresponding 'define' Hat block. This block allows users to call and reuse custom code sequences by simply dragging and dropping it into their scripts, optionally providing required input values.",
|
| 1298 |
+
"inputs": [
|
| 1299 |
+
{
|
| 1300 |
+
"name": "argument_name_1",
|
| 1301 |
+
"type": "any"
|
| 1302 |
+
},
|
| 1303 |
+
{
|
| 1304 |
+
"name": "argument_name_2",
|
| 1305 |
+
"type": "any"
|
| 1306 |
+
}
|
| 1307 |
+
],
|
| 1308 |
+
"example_standalone": "jump (50)",
|
| 1309 |
+
"example_with_other_blocks": [
|
| 1310 |
+
{
|
| 1311 |
+
"script": "when green flag clicked\n go to x: (0) y: (0)\n jump (50)\n wait (1) seconds\n say [I jumped!] for (2) seconds",
|
| 1312 |
+
"explanation": "This script moves the sprite to a starting position, then calls the 'jump' custom block with an input of 50 (assuming 'jump' is a custom block that moves the sprite up and down). After the jump, the sprite says 'I jumped!'."
|
| 1313 |
+
},
|
| 1314 |
+
{
|
| 1315 |
+
"script": "when green flag clicked\n hide\n forever\n create clone of [myself v]\n wait (1) seconds\n end",
|
| 1316 |
+
"explanation": "This script continuously creates new clones of the current sprite every second after the original sprite hides itself."
|
| 1317 |
+
}
|
| 1318 |
+
]
|
| 1319 |
+
}
|
| 1320 |
+
]
|
| 1321 |
+
}
|
chat_agent/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
# Multi-language Chat Agent Package
|
chat_agent/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (152 Bytes). View file
|
|
|
chat_agent/api/README.md
ADDED
|
@@ -0,0 +1,898 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Chat Agent API Documentation
|
| 2 |
+
|
| 3 |
+
This document describes the REST API endpoints for the multi-language chat agent.
|
| 4 |
+
|
| 5 |
+
## Base URL
|
| 6 |
+
|
| 7 |
+
All API endpoints are prefixed with `/api/v1/chat`
|
| 8 |
+
|
| 9 |
+
## Authentication
|
| 10 |
+
|
| 11 |
+
All endpoints (except health check and supported languages) require authentication via one of the following methods:
|
| 12 |
+
|
| 13 |
+
### Header-based Authentication (Development)
|
| 14 |
+
```
|
| 15 |
+
X-User-ID: your-user-id
|
| 16 |
+
```
|
| 17 |
+
|
| 18 |
+
### Token-based Authentication (Production)
|
| 19 |
+
```
|
| 20 |
+
Authorization: Bearer your-session-token
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
## Rate Limiting
|
| 24 |
+
|
| 25 |
+
The API implements rate limiting to prevent abuse:
|
| 26 |
+
- Default: 200 requests per day, 50 requests per hour
|
| 27 |
+
- Session creation: 10 requests per minute
|
| 28 |
+
- Session deletion: 5 requests per minute
|
| 29 |
+
- Other endpoints: 20-30 requests per minute
|
| 30 |
+
|
| 31 |
+
Rate limit exceeded responses return HTTP 429 with retry information.
|
| 32 |
+
|
| 33 |
+
## Endpoints
|
| 34 |
+
|
| 35 |
+
### 1. Get Supported Languages
|
| 36 |
+
|
| 37 |
+
Get a list of all supported programming languages.
|
| 38 |
+
|
| 39 |
+
**Endpoint:** `GET /api/v1/chat/languages`
|
| 40 |
+
|
| 41 |
+
**Authentication:** Not required
|
| 42 |
+
|
| 43 |
+
**Response:**
|
| 44 |
+
```json
|
| 45 |
+
{
|
| 46 |
+
"languages": [
|
| 47 |
+
{
|
| 48 |
+
"code": "python",
|
| 49 |
+
"name": "Python",
|
| 50 |
+
"syntax_highlighting": "python",
|
| 51 |
+
"file_extensions": [".py", ".pyw"]
|
| 52 |
+
},
|
| 53 |
+
{
|
| 54 |
+
"code": "javascript",
|
| 55 |
+
"name": "JavaScript",
|
| 56 |
+
"syntax_highlighting": "javascript",
|
| 57 |
+
"file_extensions": [".js", ".mjs"]
|
| 58 |
+
}
|
| 59 |
+
],
|
| 60 |
+
"default_language": "python",
|
| 61 |
+
"total_count": 8
|
| 62 |
+
}
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
### 2. Create Chat Session
|
| 66 |
+
|
| 67 |
+
Create a new chat session for a user.
|
| 68 |
+
|
| 69 |
+
**Endpoint:** `POST /api/v1/chat/sessions`
|
| 70 |
+
|
| 71 |
+
**Authentication:** Required
|
| 72 |
+
|
| 73 |
+
**Request Body:**
|
| 74 |
+
```json
|
| 75 |
+
{
|
| 76 |
+
"language": "python",
|
| 77 |
+
"metadata": {
|
| 78 |
+
"source": "web_app",
|
| 79 |
+
"user_preferences": {}
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
**Response (201 Created):**
|
| 85 |
+
```json
|
| 86 |
+
{
|
| 87 |
+
"session_id": "uuid-string",
|
| 88 |
+
"user_id": "user-123",
|
| 89 |
+
"language": "python",
|
| 90 |
+
"created_at": "2023-01-01T00:00:00",
|
| 91 |
+
"message_count": 0,
|
| 92 |
+
"metadata": {
|
| 93 |
+
"source": "web_app"
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
**Error Responses:**
|
| 99 |
+
- `400 Bad Request`: Invalid language or missing required fields
|
| 100 |
+
- `401 Unauthorized`: Missing or invalid authentication
|
| 101 |
+
- `429 Too Many Requests`: Rate limit exceeded
|
| 102 |
+
|
| 103 |
+
### 3. Get Chat Session
|
| 104 |
+
|
| 105 |
+
Retrieve information about a specific chat session.
|
| 106 |
+
|
| 107 |
+
**Endpoint:** `GET /api/v1/chat/sessions/{session_id}`
|
| 108 |
+
|
| 109 |
+
**Authentication:** Required (must own the session)
|
| 110 |
+
|
| 111 |
+
**Response (200 OK):**
|
| 112 |
+
```json
|
| 113 |
+
{
|
| 114 |
+
"session_id": "uuid-string",
|
| 115 |
+
"user_id": "user-123",
|
| 116 |
+
"language": "python",
|
| 117 |
+
"created_at": "2023-01-01T00:00:00",
|
| 118 |
+
"last_active": "2023-01-01T01:00:00",
|
| 119 |
+
"message_count": 5,
|
| 120 |
+
"is_active": true,
|
| 121 |
+
"metadata": {}
|
| 122 |
+
}
|
| 123 |
+
```
|
| 124 |
+
|
| 125 |
+
**Error Responses:**
|
| 126 |
+
- `403 Forbidden`: Session belongs to different user
|
| 127 |
+
- `404 Not Found`: Session does not exist
|
| 128 |
+
- `410 Gone`: Session has expired
|
| 129 |
+
|
| 130 |
+
### 4. List User Sessions
|
| 131 |
+
|
| 132 |
+
Get all sessions for the authenticated user.
|
| 133 |
+
|
| 134 |
+
**Endpoint:** `GET /api/v1/chat/sessions`
|
| 135 |
+
|
| 136 |
+
**Authentication:** Required
|
| 137 |
+
|
| 138 |
+
**Query Parameters:**
|
| 139 |
+
- `active_only` (boolean, default: true): Only return active sessions
|
| 140 |
+
|
| 141 |
+
**Response (200 OK):**
|
| 142 |
+
```json
|
| 143 |
+
{
|
| 144 |
+
"sessions": [
|
| 145 |
+
{
|
| 146 |
+
"session_id": "uuid-string",
|
| 147 |
+
"language": "python",
|
| 148 |
+
"created_at": "2023-01-01T00:00:00",
|
| 149 |
+
"last_active": "2023-01-01T01:00:00",
|
| 150 |
+
"message_count": 5,
|
| 151 |
+
"is_active": true,
|
| 152 |
+
"metadata": {}
|
| 153 |
+
}
|
| 154 |
+
],
|
| 155 |
+
"total_count": 1,
|
| 156 |
+
"active_only": true
|
| 157 |
+
}
|
| 158 |
+
```
|
| 159 |
+
|
| 160 |
+
### 5. Delete Chat Session
|
| 161 |
+
|
| 162 |
+
Delete a chat session and all associated data.
|
| 163 |
+
|
| 164 |
+
**Endpoint:** `DELETE /api/v1/chat/sessions/{session_id}`
|
| 165 |
+
|
| 166 |
+
**Authentication:** Required (must own the session)
|
| 167 |
+
|
| 168 |
+
**Response (200 OK):**
|
| 169 |
+
```json
|
| 170 |
+
{
|
| 171 |
+
"message": "Session deleted successfully",
|
| 172 |
+
"session_id": "uuid-string",
|
| 173 |
+
"messages_deleted": 10
|
| 174 |
+
}
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
**Error Responses:**
|
| 178 |
+
- `403 Forbidden`: Session belongs to different user
|
| 179 |
+
- `404 Not Found`: Session does not exist
|
| 180 |
+
|
| 181 |
+
### 6. Get Chat History
|
| 182 |
+
|
| 183 |
+
Retrieve chat history for a session.
|
| 184 |
+
|
| 185 |
+
**Endpoint:** `GET /api/v1/chat/sessions/{session_id}/history`
|
| 186 |
+
|
| 187 |
+
**Authentication:** Required (must own the session)
|
| 188 |
+
|
| 189 |
+
**Query Parameters:**
|
| 190 |
+
- `page` (integer, default: 1): Page number for pagination
|
| 191 |
+
- `page_size` (integer, default: 50, max: 100): Messages per page
|
| 192 |
+
- `recent_only` (boolean, default: false): Get only recent messages
|
| 193 |
+
- `limit` (integer, default: 10, max: 50): Number of recent messages (when recent_only=true)
|
| 194 |
+
|
| 195 |
+
**Response (200 OK):**
|
| 196 |
+
```json
|
| 197 |
+
{
|
| 198 |
+
"messages": [
|
| 199 |
+
{
|
| 200 |
+
"id": "uuid-string",
|
| 201 |
+
"role": "user",
|
| 202 |
+
"content": "Hello, can you help me with Python?",
|
| 203 |
+
"language": "python",
|
| 204 |
+
"timestamp": "2023-01-01T00:00:00",
|
| 205 |
+
"metadata": {}
|
| 206 |
+
},
|
| 207 |
+
{
|
| 208 |
+
"id": "uuid-string",
|
| 209 |
+
"role": "assistant",
|
| 210 |
+
"content": "Of course! I'd be happy to help you with Python.",
|
| 211 |
+
"language": "python",
|
| 212 |
+
"timestamp": "2023-01-01T00:01:00",
|
| 213 |
+
"metadata": {
|
| 214 |
+
"tokens": 15
|
| 215 |
+
}
|
| 216 |
+
}
|
| 217 |
+
],
|
| 218 |
+
"session_id": "uuid-string",
|
| 219 |
+
"total_count": 2,
|
| 220 |
+
"page": 1,
|
| 221 |
+
"page_size": 50,
|
| 222 |
+
"total_pages": 1
|
| 223 |
+
}
|
| 224 |
+
```
|
| 225 |
+
|
| 226 |
+
### 7. Search Chat History
|
| 227 |
+
|
| 228 |
+
Search messages within a session.
|
| 229 |
+
|
| 230 |
+
**Endpoint:** `GET /api/v1/chat/sessions/{session_id}/history/search`
|
| 231 |
+
|
| 232 |
+
**Authentication:** Required (must own the session)
|
| 233 |
+
|
| 234 |
+
**Query Parameters:**
|
| 235 |
+
- `q` (string, required, min: 3 chars): Search query
|
| 236 |
+
- `limit` (integer, default: 20, max: 50): Maximum results
|
| 237 |
+
|
| 238 |
+
**Response (200 OK):**
|
| 239 |
+
```json
|
| 240 |
+
{
|
| 241 |
+
"messages": [
|
| 242 |
+
{
|
| 243 |
+
"id": "uuid-string",
|
| 244 |
+
"role": "user",
|
| 245 |
+
"content": "How do I use Python lists?",
|
| 246 |
+
"language": "python",
|
| 247 |
+
"timestamp": "2023-01-01T00:00:00",
|
| 248 |
+
"metadata": {}
|
| 249 |
+
}
|
| 250 |
+
],
|
| 251 |
+
"session_id": "uuid-string",
|
| 252 |
+
"query": "Python",
|
| 253 |
+
"result_count": 1
|
| 254 |
+
}
|
| 255 |
+
```
|
| 256 |
+
|
| 257 |
+
**Error Responses:**
|
| 258 |
+
- `400 Bad Request`: Query too short or missing
|
| 259 |
+
|
| 260 |
+
### 8. Get Language Context
|
| 261 |
+
|
| 262 |
+
Get the current language context for a session.
|
| 263 |
+
|
| 264 |
+
**Endpoint:** `GET /api/v1/chat/sessions/{session_id}/language`
|
| 265 |
+
|
| 266 |
+
**Authentication:** Required (must own the session)
|
| 267 |
+
|
| 268 |
+
**Response (200 OK):**
|
| 269 |
+
```json
|
| 270 |
+
{
|
| 271 |
+
"session_id": "uuid-string",
|
| 272 |
+
"language": "python",
|
| 273 |
+
"prompt_template": "You are a helpful Python programming assistant...",
|
| 274 |
+
"syntax_highlighting": "python",
|
| 275 |
+
"language_info": {
|
| 276 |
+
"name": "Python",
|
| 277 |
+
"syntax_highlighting": "python",
|
| 278 |
+
"file_extensions": [".py", ".pyw"],
|
| 279 |
+
"prompt_template": "..."
|
| 280 |
+
},
|
| 281 |
+
"updated_at": "2023-01-01T00:00:00"
|
| 282 |
+
}
|
| 283 |
+
```
|
| 284 |
+
|
| 285 |
+
### 9. Update Language Context
|
| 286 |
+
|
| 287 |
+
Change the programming language for a session.
|
| 288 |
+
|
| 289 |
+
**Endpoint:** `PUT /api/v1/chat/sessions/{session_id}/language`
|
| 290 |
+
|
| 291 |
+
**Authentication:** Required (must own the session)
|
| 292 |
+
|
| 293 |
+
**Request Body:**
|
| 294 |
+
```json
|
| 295 |
+
{
|
| 296 |
+
"language": "javascript"
|
| 297 |
+
}
|
| 298 |
+
```
|
| 299 |
+
|
| 300 |
+
**Response (200 OK):**
|
| 301 |
+
```json
|
| 302 |
+
{
|
| 303 |
+
"session_id": "uuid-string",
|
| 304 |
+
"language": "javascript",
|
| 305 |
+
"prompt_template": "You are a helpful JavaScript programming assistant...",
|
| 306 |
+
"syntax_highlighting": "javascript",
|
| 307 |
+
"language_info": {
|
| 308 |
+
"name": "JavaScript",
|
| 309 |
+
"syntax_highlighting": "javascript",
|
| 310 |
+
"file_extensions": [".js", ".mjs"],
|
| 311 |
+
"prompt_template": "..."
|
| 312 |
+
},
|
| 313 |
+
"updated_at": "2023-01-01T01:00:00"
|
| 314 |
+
}
|
| 315 |
+
```
|
| 316 |
+
|
| 317 |
+
**Error Responses:**
|
| 318 |
+
- `400 Bad Request`: Unsupported language
|
| 319 |
+
|
| 320 |
+
### 10. Health Check
|
| 321 |
+
|
| 322 |
+
Check the health status of the API and its dependencies.
|
| 323 |
+
|
| 324 |
+
**Endpoint:** `GET /api/v1/chat/health`
|
| 325 |
+
|
| 326 |
+
**Authentication:** Not required
|
| 327 |
+
|
| 328 |
+
**Response (200 OK):**
|
| 329 |
+
```json
|
| 330 |
+
{
|
| 331 |
+
"status": "healthy",
|
| 332 |
+
"timestamp": "2023-01-01T00:00:00",
|
| 333 |
+
"services": {
|
| 334 |
+
"database": "connected",
|
| 335 |
+
"redis": "connected"
|
| 336 |
+
}
|
| 337 |
+
}
|
| 338 |
+
```
|
| 339 |
+
|
| 340 |
+
**Response (503 Service Unavailable):**
|
| 341 |
+
```json
|
| 342 |
+
{
|
| 343 |
+
"status": "unhealthy",
|
| 344 |
+
"timestamp": "2023-01-01T00:00:00",
|
| 345 |
+
"error": "Database connection failed"
|
| 346 |
+
}
|
| 347 |
+
```
|
| 348 |
+
|
| 349 |
+
## Error Responses
|
| 350 |
+
|
| 351 |
+
All error responses follow a consistent format:
|
| 352 |
+
|
| 353 |
+
```json
|
| 354 |
+
{
|
| 355 |
+
"error": "Error type",
|
| 356 |
+
"message": "Detailed error message"
|
| 357 |
+
}
|
| 358 |
+
```
|
| 359 |
+
|
| 360 |
+
### Common HTTP Status Codes
|
| 361 |
+
|
| 362 |
+
- `200 OK`: Request successful
|
| 363 |
+
- `201 Created`: Resource created successfully
|
| 364 |
+
- `400 Bad Request`: Invalid request data
|
| 365 |
+
- `401 Unauthorized`: Authentication required
|
| 366 |
+
- `403 Forbidden`: Access denied
|
| 367 |
+
- `404 Not Found`: Resource not found
|
| 368 |
+
- `410 Gone`: Resource expired
|
| 369 |
+
- `429 Too Many Requests`: Rate limit exceeded
|
| 370 |
+
- `500 Internal Server Error`: Server error
|
| 371 |
+
- `503 Service Unavailable`: Service temporarily unavailable
|
| 372 |
+
|
| 373 |
+
## Security Considerations
|
| 374 |
+
|
| 375 |
+
1. **Authentication**: All endpoints require valid authentication
|
| 376 |
+
2. **Authorization**: Users can only access their own sessions
|
| 377 |
+
3. **Rate Limiting**: Prevents abuse and manages API costs
|
| 378 |
+
4. **Input Validation**: All inputs are validated and sanitized
|
| 379 |
+
5. **Error Handling**: Errors don't expose sensitive information
|
| 380 |
+
|
| 381 |
+
## Usage Examples
|
| 382 |
+
|
| 383 |
+
### Create a Session and Send Messages
|
| 384 |
+
|
| 385 |
+
```bash
|
| 386 |
+
# Create session
|
| 387 |
+
curl -X POST http://localhost:5000/api/v1/chat/sessions \
|
| 388 |
+
-H "X-User-ID: user-123" \
|
| 389 |
+
-H "Content-Type: application/json" \
|
| 390 |
+
-d '{"language": "python"}'
|
| 391 |
+
|
| 392 |
+
# Get session info
|
| 393 |
+
curl -X GET http://localhost:5000/api/v1/chat/sessions/{session_id} \
|
| 394 |
+
-H "X-User-ID: user-123"
|
| 395 |
+
|
| 396 |
+
# Change language
|
| 397 |
+
curl -X PUT http://localhost:5000/api/v1/chat/sessions/{session_id}/language \
|
| 398 |
+
-H "X-User-ID: user-123" \
|
| 399 |
+
-H "Content-Type: application/json" \
|
| 400 |
+
-d '{"language": "javascript"}'
|
| 401 |
+
|
| 402 |
+
# Get chat history
|
| 403 |
+
curl -X GET http://localhost:5000/api/v1/chat/sessions/{session_id}/history \
|
| 404 |
+
-H "X-User-ID: user-123"
|
| 405 |
+
|
| 406 |
+
# Delete session
|
| 407 |
+
curl -X DELETE http://localhost:5000/api/v1/chat/sessions/{session_id} \
|
| 408 |
+
-H "X-User-ID: user-123"
|
| 409 |
+
```
|
| 410 |
+
|
| 411 |
+
### JavaScript/TypeScript Example
|
| 412 |
+
|
| 413 |
+
```javascript
|
| 414 |
+
const API_BASE = 'http://localhost:5000/api/v1/chat';
|
| 415 |
+
const USER_ID = 'user-123';
|
| 416 |
+
|
| 417 |
+
// Create session
|
| 418 |
+
const response = await fetch(`${API_BASE}/sessions`, {
|
| 419 |
+
method: 'POST',
|
| 420 |
+
headers: {
|
| 421 |
+
'X-User-ID': USER_ID,
|
| 422 |
+
'Content-Type': 'application/json'
|
| 423 |
+
},
|
| 424 |
+
body: JSON.stringify({
|
| 425 |
+
language: 'python',
|
| 426 |
+
metadata: { source: 'web_app' }
|
| 427 |
+
})
|
| 428 |
+
});
|
| 429 |
+
|
| 430 |
+
const session = await response.json();
|
| 431 |
+
console.log('Created session:', session.session_id);
|
| 432 |
+
|
| 433 |
+
// Get chat history
|
| 434 |
+
const historyResponse = await fetch(
|
| 435 |
+
`${API_BASE}/sessions/${session.session_id}/history`,
|
| 436 |
+
{
|
| 437 |
+
headers: { 'X-User-ID': USER_ID }
|
| 438 |
+
}
|
| 439 |
+
);
|
| 440 |
+
|
| 441 |
+
const history = await historyResponse.json();
|
| 442 |
+
console.log('Messages:', history.messages);
|
| 443 |
+
```
|
| 444 |
+
|
| 445 |
+
## WebSocket API Documentation
|
| 446 |
+
|
| 447 |
+
The chat agent also supports real-time communication via WebSocket for interactive chat sessions.
|
| 448 |
+
|
| 449 |
+
### WebSocket Connection
|
| 450 |
+
|
| 451 |
+
**Endpoint:** `ws://localhost:5000/socket.io/`
|
| 452 |
+
|
| 453 |
+
**Authentication:** Include user ID in connection query parameters or headers
|
| 454 |
+
|
| 455 |
+
**Connection Example:**
|
| 456 |
+
```javascript
|
| 457 |
+
const socket = io('http://localhost:5000', {
|
| 458 |
+
query: { user_id: 'user-123' },
|
| 459 |
+
transports: ['websocket']
|
| 460 |
+
});
|
| 461 |
+
```
|
| 462 |
+
|
| 463 |
+
### WebSocket Events
|
| 464 |
+
|
| 465 |
+
#### 1. Connect Event
|
| 466 |
+
Automatically triggered when client connects.
|
| 467 |
+
|
| 468 |
+
**Client sends:**
|
| 469 |
+
```javascript
|
| 470 |
+
// Connection is automatic, but you can send auth data
|
| 471 |
+
socket.emit('authenticate', {
|
| 472 |
+
user_id: 'user-123',
|
| 473 |
+
session_token: 'optional-token'
|
| 474 |
+
});
|
| 475 |
+
```
|
| 476 |
+
|
| 477 |
+
**Server responds:**
|
| 478 |
+
```javascript
|
| 479 |
+
socket.on('connected', (data) => {
|
| 480 |
+
console.log(data);
|
| 481 |
+
// {
|
| 482 |
+
// "status": "connected",
|
| 483 |
+
// "user_id": "user-123",
|
| 484 |
+
// "timestamp": "2023-01-01T00:00:00Z"
|
| 485 |
+
// }
|
| 486 |
+
});
|
| 487 |
+
```
|
| 488 |
+
|
| 489 |
+
#### 2. Send Message Event
|
| 490 |
+
Send a chat message to the assistant.
|
| 491 |
+
|
| 492 |
+
**Client sends:**
|
| 493 |
+
```javascript
|
| 494 |
+
socket.emit('message', {
|
| 495 |
+
session_id: 'session-uuid',
|
| 496 |
+
content: 'How do I create a Python list?',
|
| 497 |
+
language: 'python',
|
| 498 |
+
metadata: {
|
| 499 |
+
source: 'web_chat',
|
| 500 |
+
timestamp: new Date().toISOString()
|
| 501 |
+
}
|
| 502 |
+
});
|
| 503 |
+
```
|
| 504 |
+
|
| 505 |
+
**Server responds:**
|
| 506 |
+
```javascript
|
| 507 |
+
socket.on('message_response', (data) => {
|
| 508 |
+
console.log(data);
|
| 509 |
+
// {
|
| 510 |
+
// "session_id": "session-uuid",
|
| 511 |
+
// "message_id": "msg-uuid",
|
| 512 |
+
// "content": "To create a Python list, you can use square brackets...",
|
| 513 |
+
// "language": "python",
|
| 514 |
+
// "timestamp": "2023-01-01T00:01:00Z",
|
| 515 |
+
// "metadata": {
|
| 516 |
+
// "tokens": 45,
|
| 517 |
+
// "response_time": 0.85
|
| 518 |
+
// }
|
| 519 |
+
// }
|
| 520 |
+
});
|
| 521 |
+
```
|
| 522 |
+
|
| 523 |
+
#### 3. Streaming Response Event
|
| 524 |
+
For real-time streaming responses.
|
| 525 |
+
|
| 526 |
+
**Client sends:**
|
| 527 |
+
```javascript
|
| 528 |
+
socket.emit('message_stream', {
|
| 529 |
+
session_id: 'session-uuid',
|
| 530 |
+
content: 'Explain Python functions in detail',
|
| 531 |
+
language: 'python'
|
| 532 |
+
});
|
| 533 |
+
```
|
| 534 |
+
|
| 535 |
+
**Server responds with multiple events:**
|
| 536 |
+
```javascript
|
| 537 |
+
socket.on('stream_start', (data) => {
|
| 538 |
+
// { "session_id": "session-uuid", "message_id": "msg-uuid" }
|
| 539 |
+
});
|
| 540 |
+
|
| 541 |
+
socket.on('stream_chunk', (data) => {
|
| 542 |
+
// { "session_id": "session-uuid", "chunk": "A Python function is..." }
|
| 543 |
+
});
|
| 544 |
+
|
| 545 |
+
socket.on('stream_end', (data) => {
|
| 546 |
+
// { "session_id": "session-uuid", "complete_response": "..." }
|
| 547 |
+
});
|
| 548 |
+
```
|
| 549 |
+
|
| 550 |
+
#### 4. Language Switch Event
|
| 551 |
+
Change the programming language context.
|
| 552 |
+
|
| 553 |
+
**Client sends:**
|
| 554 |
+
```javascript
|
| 555 |
+
socket.emit('language_switch', {
|
| 556 |
+
session_id: 'session-uuid',
|
| 557 |
+
language: 'javascript'
|
| 558 |
+
});
|
| 559 |
+
```
|
| 560 |
+
|
| 561 |
+
**Server responds:**
|
| 562 |
+
```javascript
|
| 563 |
+
socket.on('language_switched', (data) => {
|
| 564 |
+
// {
|
| 565 |
+
// "session_id": "session-uuid",
|
| 566 |
+
// "previous_language": "python",
|
| 567 |
+
// "new_language": "javascript",
|
| 568 |
+
// "timestamp": "2023-01-01T00:02:00Z"
|
| 569 |
+
// }
|
| 570 |
+
});
|
| 571 |
+
```
|
| 572 |
+
|
| 573 |
+
#### 5. Typing Indicator Events
|
| 574 |
+
Show when user or assistant is typing.
|
| 575 |
+
|
| 576 |
+
**Client sends:**
|
| 577 |
+
```javascript
|
| 578 |
+
socket.emit('typing_start', {
|
| 579 |
+
session_id: 'session-uuid'
|
| 580 |
+
});
|
| 581 |
+
|
| 582 |
+
socket.emit('typing_stop', {
|
| 583 |
+
session_id: 'session-uuid'
|
| 584 |
+
});
|
| 585 |
+
```
|
| 586 |
+
|
| 587 |
+
**Server responds:**
|
| 588 |
+
```javascript
|
| 589 |
+
socket.on('assistant_typing', (data) => {
|
| 590 |
+
// { "session_id": "session-uuid", "typing": true }
|
| 591 |
+
});
|
| 592 |
+
|
| 593 |
+
socket.on('assistant_typing_stop', (data) => {
|
| 594 |
+
// { "session_id": "session-uuid", "typing": false }
|
| 595 |
+
});
|
| 596 |
+
```
|
| 597 |
+
|
| 598 |
+
#### 6. Error Events
|
| 599 |
+
Handle various error conditions.
|
| 600 |
+
|
| 601 |
+
**Server sends:**
|
| 602 |
+
```javascript
|
| 603 |
+
socket.on('error', (data) => {
|
| 604 |
+
console.error(data);
|
| 605 |
+
// {
|
| 606 |
+
// "error": "session_not_found",
|
| 607 |
+
// "message": "Session does not exist or has expired",
|
| 608 |
+
// "session_id": "session-uuid",
|
| 609 |
+
// "timestamp": "2023-01-01T00:03:00Z"
|
| 610 |
+
// }
|
| 611 |
+
});
|
| 612 |
+
```
|
| 613 |
+
|
| 614 |
+
### Complete WebSocket Example
|
| 615 |
+
|
| 616 |
+
```html
|
| 617 |
+
<!DOCTYPE html>
|
| 618 |
+
<html>
|
| 619 |
+
<head>
|
| 620 |
+
<title>Chat Agent WebSocket Example</title>
|
| 621 |
+
<script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
|
| 622 |
+
</head>
|
| 623 |
+
<body>
|
| 624 |
+
<div id="messages"></div>
|
| 625 |
+
<input type="text" id="messageInput" placeholder="Type your message...">
|
| 626 |
+
<select id="languageSelect">
|
| 627 |
+
<option value="python">Python</option>
|
| 628 |
+
<option value="javascript">JavaScript</option>
|
| 629 |
+
<option value="java">Java</option>
|
| 630 |
+
</select>
|
| 631 |
+
<button onclick="sendMessage()">Send</button>
|
| 632 |
+
|
| 633 |
+
<script>
|
| 634 |
+
const socket = io('http://localhost:5000', {
|
| 635 |
+
query: { user_id: 'demo-user' }
|
| 636 |
+
});
|
| 637 |
+
|
| 638 |
+
let currentSessionId = null;
|
| 639 |
+
|
| 640 |
+
// Create session first
|
| 641 |
+
fetch('/api/v1/chat/sessions', {
|
| 642 |
+
method: 'POST',
|
| 643 |
+
headers: {
|
| 644 |
+
'X-User-ID': 'demo-user',
|
| 645 |
+
'Content-Type': 'application/json'
|
| 646 |
+
},
|
| 647 |
+
body: JSON.stringify({ language: 'python' })
|
| 648 |
+
})
|
| 649 |
+
.then(response => response.json())
|
| 650 |
+
.then(data => {
|
| 651 |
+
currentSessionId = data.session_id;
|
| 652 |
+
console.log('Session created:', currentSessionId);
|
| 653 |
+
});
|
| 654 |
+
|
| 655 |
+
// Handle connection
|
| 656 |
+
socket.on('connected', (data) => {
|
| 657 |
+
console.log('Connected to chat agent:', data);
|
| 658 |
+
});
|
| 659 |
+
|
| 660 |
+
// Handle messages
|
| 661 |
+
socket.on('message_response', (data) => {
|
| 662 |
+
addMessage('Assistant', data.content);
|
| 663 |
+
});
|
| 664 |
+
|
| 665 |
+
// Handle streaming
|
| 666 |
+
let streamBuffer = '';
|
| 667 |
+
socket.on('stream_chunk', (data) => {
|
| 668 |
+
streamBuffer += data.chunk;
|
| 669 |
+
updateStreamingMessage(streamBuffer);
|
| 670 |
+
});
|
| 671 |
+
|
| 672 |
+
socket.on('stream_end', (data) => {
|
| 673 |
+
addMessage('Assistant', data.complete_response);
|
| 674 |
+
streamBuffer = '';
|
| 675 |
+
});
|
| 676 |
+
|
| 677 |
+
// Handle language switches
|
| 678 |
+
socket.on('language_switched', (data) => {
|
| 679 |
+
console.log('Language switched:', data);
|
| 680 |
+
addMessage('System', `Language changed to ${data.new_language}`);
|
| 681 |
+
});
|
| 682 |
+
|
| 683 |
+
// Handle errors
|
| 684 |
+
socket.on('error', (data) => {
|
| 685 |
+
console.error('Chat error:', data);
|
| 686 |
+
addMessage('Error', data.message);
|
| 687 |
+
});
|
| 688 |
+
|
| 689 |
+
function sendMessage() {
|
| 690 |
+
const input = document.getElementById('messageInput');
|
| 691 |
+
const message = input.value.trim();
|
| 692 |
+
|
| 693 |
+
if (message && currentSessionId) {
|
| 694 |
+
addMessage('You', message);
|
| 695 |
+
|
| 696 |
+
socket.emit('message', {
|
| 697 |
+
session_id: currentSessionId,
|
| 698 |
+
content: message,
|
| 699 |
+
language: document.getElementById('languageSelect').value
|
| 700 |
+
});
|
| 701 |
+
|
| 702 |
+
input.value = '';
|
| 703 |
+
}
|
| 704 |
+
}
|
| 705 |
+
|
| 706 |
+
function addMessage(sender, content) {
|
| 707 |
+
const messages = document.getElementById('messages');
|
| 708 |
+
const messageDiv = document.createElement('div');
|
| 709 |
+
messageDiv.innerHTML = `<strong>${sender}:</strong> ${content}`;
|
| 710 |
+
messages.appendChild(messageDiv);
|
| 711 |
+
messages.scrollTop = messages.scrollHeight;
|
| 712 |
+
}
|
| 713 |
+
|
| 714 |
+
// Language switching
|
| 715 |
+
document.getElementById('languageSelect').addEventListener('change', (e) => {
|
| 716 |
+
if (currentSessionId) {
|
| 717 |
+
socket.emit('language_switch', {
|
| 718 |
+
session_id: currentSessionId,
|
| 719 |
+
language: e.target.value
|
| 720 |
+
});
|
| 721 |
+
}
|
| 722 |
+
});
|
| 723 |
+
|
| 724 |
+
// Enter key to send message
|
| 725 |
+
document.getElementById('messageInput').addEventListener('keypress', (e) => {
|
| 726 |
+
if (e.key === 'Enter') {
|
| 727 |
+
sendMessage();
|
| 728 |
+
}
|
| 729 |
+
});
|
| 730 |
+
</script>
|
| 731 |
+
</body>
|
| 732 |
+
</html>
|
| 733 |
+
```
|
| 734 |
+
|
| 735 |
+
## Advanced Usage Examples
|
| 736 |
+
|
| 737 |
+
### Error Handling and Retry Logic
|
| 738 |
+
|
| 739 |
+
```javascript
|
| 740 |
+
class ChatAPIClient {
|
| 741 |
+
constructor(baseURL, userID) {
|
| 742 |
+
this.baseURL = baseURL;
|
| 743 |
+
this.userID = userID;
|
| 744 |
+
this.maxRetries = 3;
|
| 745 |
+
this.retryDelay = 1000; // 1 second
|
| 746 |
+
}
|
| 747 |
+
|
| 748 |
+
async makeRequest(endpoint, options = {}) {
|
| 749 |
+
const url = `${this.baseURL}${endpoint}`;
|
| 750 |
+
const config = {
|
| 751 |
+
...options,
|
| 752 |
+
headers: {
|
| 753 |
+
'X-User-ID': this.userID,
|
| 754 |
+
'Content-Type': 'application/json',
|
| 755 |
+
...options.headers
|
| 756 |
+
}
|
| 757 |
+
};
|
| 758 |
+
|
| 759 |
+
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
|
| 760 |
+
try {
|
| 761 |
+
const response = await fetch(url, config);
|
| 762 |
+
|
| 763 |
+
if (response.status === 429) {
|
| 764 |
+
// Rate limited - wait and retry
|
| 765 |
+
const retryAfter = response.headers.get('Retry-After') || this.retryDelay / 1000;
|
| 766 |
+
await this.sleep(retryAfter * 1000);
|
| 767 |
+
continue;
|
| 768 |
+
}
|
| 769 |
+
|
| 770 |
+
if (!response.ok) {
|
| 771 |
+
const error = await response.json();
|
| 772 |
+
throw new Error(`API Error: ${error.message}`);
|
| 773 |
+
}
|
| 774 |
+
|
| 775 |
+
return await response.json();
|
| 776 |
+
|
| 777 |
+
} catch (error) {
|
| 778 |
+
if (attempt === this.maxRetries) {
|
| 779 |
+
throw error;
|
| 780 |
+
}
|
| 781 |
+
|
| 782 |
+
console.warn(`Attempt ${attempt} failed, retrying...`, error.message);
|
| 783 |
+
await this.sleep(this.retryDelay * attempt);
|
| 784 |
+
}
|
| 785 |
+
}
|
| 786 |
+
}
|
| 787 |
+
|
| 788 |
+
async createSession(language = 'python') {
|
| 789 |
+
return await this.makeRequest('/api/v1/chat/sessions', {
|
| 790 |
+
method: 'POST',
|
| 791 |
+
body: JSON.stringify({ language })
|
| 792 |
+
});
|
| 793 |
+
}
|
| 794 |
+
|
| 795 |
+
async getChatHistory(sessionId, page = 1, pageSize = 50) {
|
| 796 |
+
return await this.makeRequest(
|
| 797 |
+
`/api/v1/chat/sessions/${sessionId}/history?page=${page}&page_size=${pageSize}`
|
| 798 |
+
);
|
| 799 |
+
}
|
| 800 |
+
|
| 801 |
+
async switchLanguage(sessionId, language) {
|
| 802 |
+
return await this.makeRequest(`/api/v1/chat/sessions/${sessionId}/language`, {
|
| 803 |
+
method: 'PUT',
|
| 804 |
+
body: JSON.stringify({ language })
|
| 805 |
+
});
|
| 806 |
+
}
|
| 807 |
+
|
| 808 |
+
sleep(ms) {
|
| 809 |
+
return new Promise(resolve => setTimeout(resolve, ms));
|
| 810 |
+
}
|
| 811 |
+
}
|
| 812 |
+
|
| 813 |
+
// Usage
|
| 814 |
+
const client = new ChatAPIClient('http://localhost:5000', 'user-123');
|
| 815 |
+
|
| 816 |
+
try {
|
| 817 |
+
const session = await client.createSession('python');
|
| 818 |
+
console.log('Session created:', session.session_id);
|
| 819 |
+
|
| 820 |
+
const history = await client.getChatHistory(session.session_id);
|
| 821 |
+
console.log('Chat history:', history.messages);
|
| 822 |
+
|
| 823 |
+
await client.switchLanguage(session.session_id, 'javascript');
|
| 824 |
+
console.log('Language switched to JavaScript');
|
| 825 |
+
|
| 826 |
+
} catch (error) {
|
| 827 |
+
console.error('API operation failed:', error.message);
|
| 828 |
+
}
|
| 829 |
+
```
|
| 830 |
+
|
| 831 |
+
### Batch Operations
|
| 832 |
+
|
| 833 |
+
```python
|
| 834 |
+
import asyncio
|
| 835 |
+
import aiohttp
|
| 836 |
+
import json
|
| 837 |
+
|
| 838 |
+
class AsyncChatClient:
|
| 839 |
+
def __init__(self, base_url, user_id):
|
| 840 |
+
self.base_url = base_url
|
| 841 |
+
self.user_id = user_id
|
| 842 |
+
self.headers = {
|
| 843 |
+
'X-User-ID': user_id,
|
| 844 |
+
'Content-Type': 'application/json'
|
| 845 |
+
}
|
| 846 |
+
|
| 847 |
+
async def create_multiple_sessions(self, languages):
|
| 848 |
+
"""Create multiple sessions concurrently."""
|
| 849 |
+
async with aiohttp.ClientSession() as session:
|
| 850 |
+
tasks = []
|
| 851 |
+
for language in languages:
|
| 852 |
+
task = self._create_session(session, language)
|
| 853 |
+
tasks.append(task)
|
| 854 |
+
|
| 855 |
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
| 856 |
+
return results
|
| 857 |
+
|
| 858 |
+
async def _create_session(self, session, language):
|
| 859 |
+
url = f"{self.base_url}/api/v1/chat/sessions"
|
| 860 |
+
data = {"language": language}
|
| 861 |
+
|
| 862 |
+
async with session.post(url, headers=self.headers, json=data) as response:
|
| 863 |
+
if response.status == 201:
|
| 864 |
+
return await response.json()
|
| 865 |
+
else:
|
| 866 |
+
error = await response.json()
|
| 867 |
+
raise Exception(f"Failed to create {language} session: {error['message']}")
|
| 868 |
+
|
| 869 |
+
# Usage
|
| 870 |
+
async def main():
|
| 871 |
+
client = AsyncChatClient('http://localhost:5000', 'batch-user')
|
| 872 |
+
languages = ['python', 'javascript', 'java', 'cpp']
|
| 873 |
+
|
| 874 |
+
sessions = await client.create_multiple_sessions(languages)
|
| 875 |
+
|
| 876 |
+
for i, session in enumerate(sessions):
|
| 877 |
+
if isinstance(session, Exception):
|
| 878 |
+
print(f"Failed to create {languages[i]} session: {session}")
|
| 879 |
+
else:
|
| 880 |
+
print(f"Created {languages[i]} session: {session['session_id']}")
|
| 881 |
+
|
| 882 |
+
# Run the async function
|
| 883 |
+
asyncio.run(main())
|
| 884 |
+
```
|
| 885 |
+
|
| 886 |
+
## Implementation Notes
|
| 887 |
+
|
| 888 |
+
- The API uses SQLite for testing and PostgreSQL for production
|
| 889 |
+
- Redis is used for caching and session management
|
| 890 |
+
- Rate limiting uses in-memory storage by default (configure Redis for production)
|
| 891 |
+
- All timestamps are in ISO 8601 format (UTC)
|
| 892 |
+
- UUIDs are used for all resource identifiers
|
| 893 |
+
- The API supports both development (header-based) and production (token-based) authentication
|
| 894 |
+
- WebSocket connections are managed using Flask-SocketIO
|
| 895 |
+
- Streaming responses use Server-Sent Events (SSE) over WebSocket
|
| 896 |
+
- All WebSocket events include session_id for proper message routing
|
| 897 |
+
- Error handling includes automatic retry logic for transient failures
|
| 898 |
+
- The system supports concurrent operations across multiple sessions
|
chat_agent/api/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Chat Agent API Components."""
|
| 2 |
+
|
| 3 |
+
from .chat_routes import chat_bp
|
| 4 |
+
from .middleware import create_limiter, setup_error_handlers, RequestLoggingMiddleware, create_auth_manager
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
'chat_bp',
|
| 8 |
+
'create_limiter',
|
| 9 |
+
'setup_error_handlers',
|
| 10 |
+
'RequestLoggingMiddleware',
|
| 11 |
+
'create_auth_manager'
|
| 12 |
+
]
|
chat_agent/api/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (446 Bytes). View file
|
|
|
chat_agent/api/__pycache__/chat_routes.cpython-312.pyc
ADDED
|
Binary file (27.8 kB). View file
|
|
|
chat_agent/api/__pycache__/health.cpython-312.pyc
ADDED
|
Binary file (9.15 kB). View file
|
|
|
chat_agent/api/__pycache__/middleware.cpython-312.pyc
ADDED
|
Binary file (17.1 kB). View file
|
|
|
chat_agent/api/__pycache__/performance_routes.cpython-312.pyc
ADDED
|
Binary file (16.2 kB). View file
|
|
|
chat_agent/api/chat_routes.py
ADDED
|
@@ -0,0 +1,642 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Chat API routes for session management and chat history."""
|
| 2 |
+
|
| 3 |
+
import logging
|
| 4 |
+
from datetime import datetime
|
| 5 |
+
from typing import Dict, Any, Optional
|
| 6 |
+
|
| 7 |
+
from flask import Blueprint, request, jsonify, current_app, g
|
| 8 |
+
from flask_limiter import Limiter
|
| 9 |
+
from flask_limiter.util import get_remote_address
|
| 10 |
+
import redis
|
| 11 |
+
|
| 12 |
+
from ..services.session_manager import SessionManager, SessionManagerError, SessionNotFoundError, SessionExpiredError
|
| 13 |
+
from ..services.chat_history import ChatHistoryManager, ChatHistoryError
|
| 14 |
+
from ..services.language_context import LanguageContextManager, LanguageContextError
|
| 15 |
+
from ..models.language_context import LanguageContext
|
| 16 |
+
from ..models.base import db
|
| 17 |
+
from .middleware import require_auth, validate_json_request, create_limiter
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
logger = logging.getLogger(__name__)
|
| 21 |
+
|
| 22 |
+
# Create blueprint
|
| 23 |
+
chat_bp = Blueprint('chat', __name__, url_prefix='/api/v1/chat')
|
| 24 |
+
|
| 25 |
+
# Initialize rate limiter (will be configured in app factory)
|
| 26 |
+
limiter = create_limiter()
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class APIError(Exception):
|
| 30 |
+
"""Base API error class."""
|
| 31 |
+
def __init__(self, message: str, status_code: int = 400, payload: Optional[Dict] = None):
|
| 32 |
+
super().__init__()
|
| 33 |
+
self.message = message
|
| 34 |
+
self.status_code = status_code
|
| 35 |
+
self.payload = payload
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def handle_api_error(error: APIError):
|
| 39 |
+
"""Handle API errors and return JSON response."""
|
| 40 |
+
response = {'error': error.message}
|
| 41 |
+
if error.payload:
|
| 42 |
+
response.update(error.payload)
|
| 43 |
+
return jsonify(response), error.status_code
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def get_services():
|
| 47 |
+
"""Get service instances from current app context."""
|
| 48 |
+
redis_client = None
|
| 49 |
+
redis_url = current_app.config.get('REDIS_URL')
|
| 50 |
+
|
| 51 |
+
if redis_url and redis_url != 'None':
|
| 52 |
+
try:
|
| 53 |
+
redis_client = redis.from_url(redis_url)
|
| 54 |
+
# Test the connection
|
| 55 |
+
redis_client.ping()
|
| 56 |
+
except Exception as e:
|
| 57 |
+
logger.warning(f"Redis connection failed: {e}. Running without Redis cache.")
|
| 58 |
+
redis_client = None
|
| 59 |
+
else:
|
| 60 |
+
logger.info("Redis disabled in configuration. Running without Redis cache.")
|
| 61 |
+
|
| 62 |
+
session_manager = SessionManager(redis_client, current_app.config.get('SESSION_TIMEOUT', 3600))
|
| 63 |
+
chat_history_manager = ChatHistoryManager(
|
| 64 |
+
redis_client,
|
| 65 |
+
current_app.config.get('MAX_CHAT_HISTORY', 20),
|
| 66 |
+
current_app.config.get('CONTEXT_WINDOW_SIZE', 10)
|
| 67 |
+
)
|
| 68 |
+
language_context_manager = LanguageContextManager()
|
| 69 |
+
|
| 70 |
+
return session_manager, chat_history_manager, language_context_manager
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
# Session Management Endpoints
|
| 74 |
+
|
| 75 |
+
@chat_bp.route('/sessions', methods=['POST'])
|
| 76 |
+
@limiter.limit("10 per minute")
|
| 77 |
+
@require_auth
|
| 78 |
+
@validate_json_request(['language'])
|
| 79 |
+
def create_session():
|
| 80 |
+
"""
|
| 81 |
+
Create a new chat session.
|
| 82 |
+
|
| 83 |
+
Request body:
|
| 84 |
+
{
|
| 85 |
+
"language": "python",
|
| 86 |
+
"metadata": {"key": "value"} // optional
|
| 87 |
+
}
|
| 88 |
+
"""
|
| 89 |
+
try:
|
| 90 |
+
data = request.json_data
|
| 91 |
+
language = data['language']
|
| 92 |
+
metadata = data.get('metadata', {})
|
| 93 |
+
|
| 94 |
+
# Validate language
|
| 95 |
+
if not LanguageContext.is_supported_language(language):
|
| 96 |
+
supported = LanguageContext.get_supported_languages()
|
| 97 |
+
raise APIError(f'Unsupported language: {language}. Supported: {", ".join(supported)}', 400)
|
| 98 |
+
|
| 99 |
+
session_manager, _, language_context_manager = get_services()
|
| 100 |
+
|
| 101 |
+
# Create session
|
| 102 |
+
session = session_manager.create_session(
|
| 103 |
+
user_id=request.user_id,
|
| 104 |
+
language=language,
|
| 105 |
+
session_metadata=metadata
|
| 106 |
+
)
|
| 107 |
+
|
| 108 |
+
# Create language context
|
| 109 |
+
language_context_manager.create_context(session.id, language)
|
| 110 |
+
|
| 111 |
+
logger.info(f"Created session {session.id} for user {request.user_id}")
|
| 112 |
+
|
| 113 |
+
return jsonify({
|
| 114 |
+
'session_id': session.id,
|
| 115 |
+
'user_id': session.user_id,
|
| 116 |
+
'language': session.language,
|
| 117 |
+
'created_at': session.created_at.isoformat(),
|
| 118 |
+
'message_count': session.message_count,
|
| 119 |
+
'metadata': session.session_metadata
|
| 120 |
+
}), 201
|
| 121 |
+
|
| 122 |
+
except (SessionManagerError, LanguageContextError) as e:
|
| 123 |
+
logger.error(f"Error creating session: {e}")
|
| 124 |
+
raise APIError(f'Failed to create session: {str(e)}', 500)
|
| 125 |
+
except APIError:
|
| 126 |
+
raise
|
| 127 |
+
except Exception as e:
|
| 128 |
+
logger.error(f"Unexpected error creating session: {e}")
|
| 129 |
+
raise APIError('Internal server error', 500)
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
@chat_bp.route('/sessions/<session_id>', methods=['GET'])
|
| 133 |
+
@limiter.limit("30 per minute")
|
| 134 |
+
@require_auth
|
| 135 |
+
def get_session(session_id: str):
|
| 136 |
+
"""Get session information."""
|
| 137 |
+
try:
|
| 138 |
+
session_manager, _, _ = get_services()
|
| 139 |
+
|
| 140 |
+
session = session_manager.get_session(session_id)
|
| 141 |
+
|
| 142 |
+
# Check if user owns this session
|
| 143 |
+
if session.user_id != request.user_id:
|
| 144 |
+
raise APIError('Access denied', 403)
|
| 145 |
+
|
| 146 |
+
return jsonify({
|
| 147 |
+
'session_id': session.id,
|
| 148 |
+
'user_id': session.user_id,
|
| 149 |
+
'language': session.language,
|
| 150 |
+
'created_at': session.created_at.isoformat(),
|
| 151 |
+
'last_active': session.last_active.isoformat(),
|
| 152 |
+
'message_count': session.message_count,
|
| 153 |
+
'is_active': session.is_active,
|
| 154 |
+
'metadata': session.session_metadata
|
| 155 |
+
})
|
| 156 |
+
|
| 157 |
+
except SessionNotFoundError:
|
| 158 |
+
raise APIError('Session not found', 404)
|
| 159 |
+
except SessionExpiredError:
|
| 160 |
+
raise APIError('Session has expired', 410)
|
| 161 |
+
except SessionManagerError as e:
|
| 162 |
+
logger.error(f"Error getting session: {e}")
|
| 163 |
+
raise APIError(f'Failed to get session: {str(e)}', 500)
|
| 164 |
+
except APIError:
|
| 165 |
+
raise
|
| 166 |
+
except Exception as e:
|
| 167 |
+
logger.error(f"Unexpected error getting session: {e}")
|
| 168 |
+
raise APIError('Internal server error', 500)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
@chat_bp.route('/sessions/<session_id>', methods=['DELETE'])
|
| 172 |
+
@limiter.limit("5 per minute")
|
| 173 |
+
@require_auth
|
| 174 |
+
def delete_session(session_id: str):
|
| 175 |
+
"""Delete a chat session."""
|
| 176 |
+
try:
|
| 177 |
+
session_manager, chat_history_manager, _ = get_services()
|
| 178 |
+
|
| 179 |
+
# Get session to check ownership
|
| 180 |
+
session = session_manager.get_session(session_id)
|
| 181 |
+
|
| 182 |
+
# Check if user owns this session
|
| 183 |
+
if session.user_id != request.user_id:
|
| 184 |
+
raise APIError('Access denied', 403)
|
| 185 |
+
|
| 186 |
+
# Clear chat history
|
| 187 |
+
message_count = chat_history_manager.clear_session_history(session_id)
|
| 188 |
+
|
| 189 |
+
# Delete session
|
| 190 |
+
session_manager.delete_session(session_id)
|
| 191 |
+
|
| 192 |
+
logger.info(f"Deleted session {session_id} with {message_count} messages")
|
| 193 |
+
|
| 194 |
+
return jsonify({
|
| 195 |
+
'message': 'Session deleted successfully',
|
| 196 |
+
'session_id': session_id,
|
| 197 |
+
'messages_deleted': message_count
|
| 198 |
+
})
|
| 199 |
+
|
| 200 |
+
except SessionNotFoundError:
|
| 201 |
+
raise APIError('Session not found', 404)
|
| 202 |
+
except (SessionManagerError, ChatHistoryError) as e:
|
| 203 |
+
logger.error(f"Error deleting session: {e}")
|
| 204 |
+
raise APIError(f'Failed to delete session: {str(e)}', 500)
|
| 205 |
+
except APIError:
|
| 206 |
+
raise
|
| 207 |
+
except Exception as e:
|
| 208 |
+
logger.error(f"Unexpected error deleting session: {e}")
|
| 209 |
+
raise APIError('Internal server error', 500)
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
@chat_bp.route('/sessions', methods=['GET'])
|
| 213 |
+
@limiter.limit("20 per minute")
|
| 214 |
+
@require_auth
|
| 215 |
+
def list_user_sessions():
|
| 216 |
+
"""List all sessions for the authenticated user."""
|
| 217 |
+
try:
|
| 218 |
+
active_only = request.args.get('active_only', 'true').lower() == 'true'
|
| 219 |
+
|
| 220 |
+
session_manager, _, _ = get_services()
|
| 221 |
+
|
| 222 |
+
sessions = session_manager.get_user_sessions(request.user_id, active_only)
|
| 223 |
+
|
| 224 |
+
session_list = []
|
| 225 |
+
for session in sessions:
|
| 226 |
+
session_list.append({
|
| 227 |
+
'session_id': session.id,
|
| 228 |
+
'language': session.language,
|
| 229 |
+
'created_at': session.created_at.isoformat(),
|
| 230 |
+
'last_active': session.last_active.isoformat(),
|
| 231 |
+
'message_count': session.message_count,
|
| 232 |
+
'is_active': session.is_active,
|
| 233 |
+
'metadata': session.session_metadata
|
| 234 |
+
})
|
| 235 |
+
|
| 236 |
+
return jsonify({
|
| 237 |
+
'sessions': session_list,
|
| 238 |
+
'total_count': len(session_list),
|
| 239 |
+
'active_only': active_only
|
| 240 |
+
})
|
| 241 |
+
|
| 242 |
+
except SessionManagerError as e:
|
| 243 |
+
logger.error(f"Error listing sessions: {e}")
|
| 244 |
+
raise APIError(f'Failed to list sessions: {str(e)}', 500)
|
| 245 |
+
except Exception as e:
|
| 246 |
+
logger.error(f"Unexpected error listing sessions: {e}")
|
| 247 |
+
raise APIError('Internal server error', 500)
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
# Chat History Endpoints
|
| 251 |
+
|
| 252 |
+
@chat_bp.route('/sessions/<session_id>/history', methods=['GET'])
|
| 253 |
+
@limiter.limit("30 per minute")
|
| 254 |
+
@require_auth
|
| 255 |
+
def get_chat_history(session_id: str):
|
| 256 |
+
"""Get chat history for a session."""
|
| 257 |
+
try:
|
| 258 |
+
# Validate session ownership
|
| 259 |
+
session_manager, chat_history_manager, _ = get_services()
|
| 260 |
+
session = session_manager.get_session(session_id)
|
| 261 |
+
|
| 262 |
+
if session.user_id != request.user_id:
|
| 263 |
+
raise APIError('Access denied', 403)
|
| 264 |
+
|
| 265 |
+
# Get pagination parameters
|
| 266 |
+
page = int(request.args.get('page', 1))
|
| 267 |
+
page_size = min(int(request.args.get('page_size', 50)), 100) # Max 100 messages per page
|
| 268 |
+
recent_only = request.args.get('recent_only', 'false').lower() == 'true'
|
| 269 |
+
|
| 270 |
+
if recent_only:
|
| 271 |
+
# Get recent messages for context
|
| 272 |
+
limit = min(int(request.args.get('limit', 10)), 50) # Max 50 recent messages
|
| 273 |
+
messages = chat_history_manager.get_recent_history(session_id, limit)
|
| 274 |
+
total_count = len(messages)
|
| 275 |
+
else:
|
| 276 |
+
# Get paginated full history
|
| 277 |
+
messages = chat_history_manager.get_full_history(session_id, page, page_size)
|
| 278 |
+
total_count = chat_history_manager.get_message_count(session_id)
|
| 279 |
+
|
| 280 |
+
message_list = []
|
| 281 |
+
for message in messages:
|
| 282 |
+
message_list.append({
|
| 283 |
+
'id': message.id,
|
| 284 |
+
'role': message.role,
|
| 285 |
+
'content': message.content,
|
| 286 |
+
'language': message.language,
|
| 287 |
+
'timestamp': message.timestamp.isoformat(),
|
| 288 |
+
'metadata': message.message_metadata
|
| 289 |
+
})
|
| 290 |
+
|
| 291 |
+
response_data = {
|
| 292 |
+
'messages': message_list,
|
| 293 |
+
'session_id': session_id,
|
| 294 |
+
'total_count': total_count
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
if not recent_only:
|
| 298 |
+
response_data.update({
|
| 299 |
+
'page': page,
|
| 300 |
+
'page_size': page_size,
|
| 301 |
+
'total_pages': (total_count + page_size - 1) // page_size
|
| 302 |
+
})
|
| 303 |
+
|
| 304 |
+
return jsonify(response_data)
|
| 305 |
+
|
| 306 |
+
except SessionNotFoundError:
|
| 307 |
+
raise APIError('Session not found', 404)
|
| 308 |
+
except SessionExpiredError:
|
| 309 |
+
raise APIError('Session has expired', 410)
|
| 310 |
+
except ChatHistoryError as e:
|
| 311 |
+
logger.error(f"Error getting chat history: {e}")
|
| 312 |
+
raise APIError(f'Failed to get chat history: {str(e)}', 500)
|
| 313 |
+
except APIError:
|
| 314 |
+
raise
|
| 315 |
+
except Exception as e:
|
| 316 |
+
logger.error(f"Unexpected error getting chat history: {e}")
|
| 317 |
+
raise APIError('Internal server error', 500)
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
@chat_bp.route('/sessions/<session_id>/history/search', methods=['GET'])
|
| 321 |
+
@limiter.limit("20 per minute")
|
| 322 |
+
@require_auth
|
| 323 |
+
def search_chat_history(session_id: str):
|
| 324 |
+
"""Search chat history for a session."""
|
| 325 |
+
try:
|
| 326 |
+
query = request.args.get('q', '').strip()
|
| 327 |
+
if not query:
|
| 328 |
+
raise APIError('Search query is required', 400)
|
| 329 |
+
|
| 330 |
+
if len(query) < 3:
|
| 331 |
+
raise APIError('Search query must be at least 3 characters', 400)
|
| 332 |
+
|
| 333 |
+
# Validate session ownership
|
| 334 |
+
session_manager, chat_history_manager, _ = get_services()
|
| 335 |
+
session = session_manager.get_session(session_id)
|
| 336 |
+
|
| 337 |
+
if session.user_id != request.user_id:
|
| 338 |
+
raise APIError('Access denied', 403)
|
| 339 |
+
|
| 340 |
+
limit = min(int(request.args.get('limit', 20)), 50) # Max 50 results
|
| 341 |
+
|
| 342 |
+
messages = chat_history_manager.search_messages(session_id, query, limit)
|
| 343 |
+
|
| 344 |
+
message_list = []
|
| 345 |
+
for message in messages:
|
| 346 |
+
message_list.append({
|
| 347 |
+
'id': message.id,
|
| 348 |
+
'role': message.role,
|
| 349 |
+
'content': message.content,
|
| 350 |
+
'language': message.language,
|
| 351 |
+
'timestamp': message.timestamp.isoformat(),
|
| 352 |
+
'metadata': message.message_metadata
|
| 353 |
+
})
|
| 354 |
+
|
| 355 |
+
return jsonify({
|
| 356 |
+
'messages': message_list,
|
| 357 |
+
'session_id': session_id,
|
| 358 |
+
'query': query,
|
| 359 |
+
'result_count': len(message_list)
|
| 360 |
+
})
|
| 361 |
+
|
| 362 |
+
except SessionNotFoundError:
|
| 363 |
+
raise APIError('Session not found', 404)
|
| 364 |
+
except SessionExpiredError:
|
| 365 |
+
raise APIError('Session has expired', 410)
|
| 366 |
+
except ChatHistoryError as e:
|
| 367 |
+
logger.error(f"Error searching chat history: {e}")
|
| 368 |
+
raise APIError(f'Failed to search chat history: {str(e)}', 500)
|
| 369 |
+
except APIError:
|
| 370 |
+
raise
|
| 371 |
+
except Exception as e:
|
| 372 |
+
logger.error(f"Unexpected error searching chat history: {e}")
|
| 373 |
+
raise APIError('Internal server error', 500)
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
# Language Context Endpoints
|
| 377 |
+
|
| 378 |
+
@chat_bp.route('/sessions/<session_id>/language', methods=['GET'])
|
| 379 |
+
@limiter.limit("30 per minute")
|
| 380 |
+
@require_auth
|
| 381 |
+
def get_language_context(session_id: str):
|
| 382 |
+
"""Get language context for a session."""
|
| 383 |
+
try:
|
| 384 |
+
# Validate session ownership
|
| 385 |
+
session_manager, _, language_context_manager = get_services()
|
| 386 |
+
session = session_manager.get_session(session_id)
|
| 387 |
+
|
| 388 |
+
if session.user_id != request.user_id:
|
| 389 |
+
raise APIError('Access denied', 403)
|
| 390 |
+
|
| 391 |
+
context = language_context_manager.get_context(session_id)
|
| 392 |
+
|
| 393 |
+
return jsonify({
|
| 394 |
+
'session_id': session_id,
|
| 395 |
+
'language': context.language,
|
| 396 |
+
'prompt_template': context.get_prompt_template(),
|
| 397 |
+
'syntax_highlighting': context.get_syntax_highlighting(),
|
| 398 |
+
'language_info': context.get_language_info(),
|
| 399 |
+
'updated_at': context.updated_at.isoformat()
|
| 400 |
+
})
|
| 401 |
+
|
| 402 |
+
except SessionNotFoundError:
|
| 403 |
+
raise APIError('Session not found', 404)
|
| 404 |
+
except SessionExpiredError:
|
| 405 |
+
raise APIError('Session has expired', 410)
|
| 406 |
+
except LanguageContextError as e:
|
| 407 |
+
logger.error(f"Error getting language context: {e}")
|
| 408 |
+
raise APIError(f'Failed to get language context: {str(e)}', 500)
|
| 409 |
+
except APIError:
|
| 410 |
+
raise
|
| 411 |
+
except Exception as e:
|
| 412 |
+
logger.error(f"Unexpected error getting language context: {e}")
|
| 413 |
+
raise APIError('Internal server error', 500)
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
@chat_bp.route('/sessions/<session_id>/language', methods=['PUT'])
|
| 417 |
+
@limiter.limit("10 per minute")
|
| 418 |
+
@require_auth
|
| 419 |
+
@validate_json_request(['language'])
|
| 420 |
+
def update_language_context(session_id: str):
|
| 421 |
+
"""Update language context for a session."""
|
| 422 |
+
try:
|
| 423 |
+
data = request.json_data
|
| 424 |
+
language = data['language']
|
| 425 |
+
|
| 426 |
+
# Validate language
|
| 427 |
+
if not LanguageContext.is_supported_language(language):
|
| 428 |
+
supported = LanguageContext.get_supported_languages()
|
| 429 |
+
raise APIError(f'Unsupported language: {language}. Supported: {", ".join(supported)}', 400)
|
| 430 |
+
|
| 431 |
+
# Validate session ownership
|
| 432 |
+
session_manager, _, language_context_manager = get_services()
|
| 433 |
+
session = session_manager.get_session(session_id)
|
| 434 |
+
|
| 435 |
+
if session.user_id != request.user_id:
|
| 436 |
+
raise APIError('Access denied', 403)
|
| 437 |
+
|
| 438 |
+
# Update language context
|
| 439 |
+
context = language_context_manager.set_language(session_id, language)
|
| 440 |
+
|
| 441 |
+
# Update session language
|
| 442 |
+
session_manager.set_session_language(session_id, language)
|
| 443 |
+
|
| 444 |
+
logger.info(f"Updated language to {language} for session {session_id}")
|
| 445 |
+
|
| 446 |
+
return jsonify({
|
| 447 |
+
'session_id': session_id,
|
| 448 |
+
'language': context.language,
|
| 449 |
+
'prompt_template': context.get_prompt_template(),
|
| 450 |
+
'syntax_highlighting': context.get_syntax_highlighting(),
|
| 451 |
+
'language_info': context.get_language_info(),
|
| 452 |
+
'updated_at': context.updated_at.isoformat()
|
| 453 |
+
})
|
| 454 |
+
|
| 455 |
+
except SessionNotFoundError:
|
| 456 |
+
raise APIError('Session not found', 404)
|
| 457 |
+
except SessionExpiredError:
|
| 458 |
+
raise APIError('Session has expired', 410)
|
| 459 |
+
except (SessionManagerError, LanguageContextError) as e:
|
| 460 |
+
logger.error(f"Error updating language context: {e}")
|
| 461 |
+
raise APIError(f'Failed to update language context: {str(e)}', 500)
|
| 462 |
+
except APIError:
|
| 463 |
+
raise
|
| 464 |
+
except Exception as e:
|
| 465 |
+
logger.error(f"Unexpected error updating language context: {e}")
|
| 466 |
+
raise APIError('Internal server error', 500)
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
@chat_bp.route('/languages', methods=['GET'])
|
| 470 |
+
@limiter.limit("50 per minute")
|
| 471 |
+
def get_supported_languages():
|
| 472 |
+
"""Get list of supported programming languages."""
|
| 473 |
+
try:
|
| 474 |
+
languages = LanguageContext.get_supported_languages()
|
| 475 |
+
language_names = LanguageContext.get_language_display_names()
|
| 476 |
+
|
| 477 |
+
language_list = []
|
| 478 |
+
for lang_code in languages:
|
| 479 |
+
lang_info = LanguageContext.SUPPORTED_LANGUAGES[lang_code]
|
| 480 |
+
language_list.append({
|
| 481 |
+
'code': lang_code,
|
| 482 |
+
'name': lang_info['name'],
|
| 483 |
+
'syntax_highlighting': lang_info['syntax_highlighting'],
|
| 484 |
+
'file_extensions': lang_info['file_extensions']
|
| 485 |
+
})
|
| 486 |
+
|
| 487 |
+
return jsonify({
|
| 488 |
+
'languages': language_list,
|
| 489 |
+
'default_language': 'python',
|
| 490 |
+
'total_count': len(language_list)
|
| 491 |
+
})
|
| 492 |
+
|
| 493 |
+
except Exception as e:
|
| 494 |
+
logger.error(f"Unexpected error getting supported languages: {e}")
|
| 495 |
+
raise APIError('Internal server error', 500)
|
| 496 |
+
|
| 497 |
+
|
| 498 |
+
# Message Processing Endpoint
|
| 499 |
+
|
| 500 |
+
@chat_bp.route('/sessions/<session_id>/message', methods=['POST'])
|
| 501 |
+
@limiter.limit("30 per minute")
|
| 502 |
+
@require_auth
|
| 503 |
+
@validate_json_request(['content'])
|
| 504 |
+
def send_message(session_id: str):
|
| 505 |
+
"""Send a message to the chat agent and get a response."""
|
| 506 |
+
try:
|
| 507 |
+
data = request.json_data
|
| 508 |
+
content = data['content'].strip()
|
| 509 |
+
language = data.get('language') # Optional language override
|
| 510 |
+
|
| 511 |
+
if not content:
|
| 512 |
+
raise APIError('Message content cannot be empty', 400)
|
| 513 |
+
|
| 514 |
+
if len(content) > 5000: # Reasonable message length limit
|
| 515 |
+
raise APIError('Message too long (max 5000 characters)', 400)
|
| 516 |
+
|
| 517 |
+
# Get services
|
| 518 |
+
session_manager, chat_history_manager, language_context_manager = get_services()
|
| 519 |
+
|
| 520 |
+
# Validate session ownership
|
| 521 |
+
session = session_manager.get_session(session_id)
|
| 522 |
+
if session.user_id != request.user_id:
|
| 523 |
+
raise APIError('Access denied', 403)
|
| 524 |
+
|
| 525 |
+
# Initialize chat agent
|
| 526 |
+
from ..services.groq_client import GroqClient
|
| 527 |
+
from ..services.chat_agent import ChatAgent
|
| 528 |
+
from ..services.programming_assistance import ProgrammingAssistanceService
|
| 529 |
+
|
| 530 |
+
groq_client = GroqClient()
|
| 531 |
+
programming_assistance_service = ProgrammingAssistanceService()
|
| 532 |
+
|
| 533 |
+
chat_agent = ChatAgent(
|
| 534 |
+
groq_client=groq_client,
|
| 535 |
+
language_context_manager=language_context_manager,
|
| 536 |
+
session_manager=session_manager,
|
| 537 |
+
chat_history_manager=chat_history_manager,
|
| 538 |
+
programming_assistance_service=programming_assistance_service
|
| 539 |
+
)
|
| 540 |
+
|
| 541 |
+
# Process the message
|
| 542 |
+
result = chat_agent.process_message(session_id, content, language)
|
| 543 |
+
|
| 544 |
+
logger.info(f"Processed message for session {session_id}, response length: {len(result['response'])}")
|
| 545 |
+
|
| 546 |
+
return jsonify({
|
| 547 |
+
'response': result['response'],
|
| 548 |
+
'message_id': result['message_id'],
|
| 549 |
+
'session_id': session_id,
|
| 550 |
+
'language': result['language'],
|
| 551 |
+
'processing_time': result['processing_time'],
|
| 552 |
+
'timestamp': result['timestamp']
|
| 553 |
+
})
|
| 554 |
+
|
| 555 |
+
except SessionNotFoundError:
|
| 556 |
+
raise APIError('Session not found', 404)
|
| 557 |
+
except SessionExpiredError:
|
| 558 |
+
raise APIError('Session has expired', 410)
|
| 559 |
+
except ChatAgentError as e:
|
| 560 |
+
logger.error(f"Chat agent error: {e}")
|
| 561 |
+
raise APIError(f'Failed to process message: {str(e)}', 500)
|
| 562 |
+
except APIError:
|
| 563 |
+
raise
|
| 564 |
+
except Exception as e:
|
| 565 |
+
logger.error(f"Unexpected error processing message: {e}")
|
| 566 |
+
raise APIError('Internal server error', 500)
|
| 567 |
+
|
| 568 |
+
|
| 569 |
+
# Health Check Endpoint
|
| 570 |
+
|
| 571 |
+
@chat_bp.route('/health', methods=['GET'])
|
| 572 |
+
def health_check():
|
| 573 |
+
"""Health check endpoint for monitoring."""
|
| 574 |
+
try:
|
| 575 |
+
# Check database connection
|
| 576 |
+
from sqlalchemy import text
|
| 577 |
+
db.session.execute(text('SELECT 1'))
|
| 578 |
+
|
| 579 |
+
# Check Redis connection (if configured)
|
| 580 |
+
redis_status = "disabled"
|
| 581 |
+
redis_url = current_app.config.get('REDIS_URL')
|
| 582 |
+
if redis_url and redis_url != 'None':
|
| 583 |
+
try:
|
| 584 |
+
redis_client = redis.from_url(redis_url)
|
| 585 |
+
redis_client.ping()
|
| 586 |
+
redis_status = "connected"
|
| 587 |
+
except Exception:
|
| 588 |
+
redis_status = "disconnected"
|
| 589 |
+
else:
|
| 590 |
+
redis_status = "disabled"
|
| 591 |
+
|
| 592 |
+
return jsonify({
|
| 593 |
+
'status': 'healthy',
|
| 594 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 595 |
+
'services': {
|
| 596 |
+
'database': 'connected',
|
| 597 |
+
'redis': redis_status
|
| 598 |
+
}
|
| 599 |
+
})
|
| 600 |
+
|
| 601 |
+
except Exception as e:
|
| 602 |
+
logger.error(f"Health check failed: {e}")
|
| 603 |
+
return jsonify({
|
| 604 |
+
'status': 'unhealthy',
|
| 605 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 606 |
+
'error': str(e)
|
| 607 |
+
}), 503
|
| 608 |
+
|
| 609 |
+
|
| 610 |
+
# Error handlers
|
| 611 |
+
@chat_bp.errorhandler(APIError)
|
| 612 |
+
def handle_api_error_handler(error):
|
| 613 |
+
"""Handle APIError exceptions."""
|
| 614 |
+
return handle_api_error(error)
|
| 615 |
+
|
| 616 |
+
|
| 617 |
+
@chat_bp.errorhandler(400)
|
| 618 |
+
def handle_bad_request(error):
|
| 619 |
+
"""Handle bad request errors."""
|
| 620 |
+
return jsonify({'error': 'Bad request'}), 400
|
| 621 |
+
|
| 622 |
+
|
| 623 |
+
@chat_bp.errorhandler(404)
|
| 624 |
+
def handle_not_found(error):
|
| 625 |
+
"""Handle not found errors."""
|
| 626 |
+
return jsonify({'error': 'Not found'}), 404
|
| 627 |
+
|
| 628 |
+
|
| 629 |
+
@chat_bp.errorhandler(429)
|
| 630 |
+
def handle_rate_limit_exceeded(error):
|
| 631 |
+
"""Handle rate limit exceeded errors."""
|
| 632 |
+
return jsonify({
|
| 633 |
+
'error': 'Rate limit exceeded',
|
| 634 |
+
'message': 'Too many requests. Please try again later.'
|
| 635 |
+
}), 429
|
| 636 |
+
|
| 637 |
+
|
| 638 |
+
@chat_bp.errorhandler(500)
|
| 639 |
+
def handle_internal_error(error):
|
| 640 |
+
"""Handle internal server errors."""
|
| 641 |
+
logger.error(f"Internal server error: {error}")
|
| 642 |
+
return jsonify({'error': 'Internal server error'}), 500
|
chat_agent/api/health.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Health check endpoints for monitoring and load balancing."""
|
| 2 |
+
|
| 3 |
+
import time
|
| 4 |
+
import psutil
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
from flask import Blueprint, jsonify, current_app
|
| 7 |
+
import redis
|
| 8 |
+
import psycopg2
|
| 9 |
+
from sqlalchemy import text
|
| 10 |
+
|
| 11 |
+
from chat_agent.models.base import db
|
| 12 |
+
from chat_agent.utils.error_handler import get_error_handler
|
| 13 |
+
|
| 14 |
+
health_bp = Blueprint('health', __name__, url_prefix='/health')
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def check_database():
|
| 18 |
+
"""Check database connectivity and basic operations."""
|
| 19 |
+
try:
|
| 20 |
+
# Test basic database connection
|
| 21 |
+
result = db.session.execute(text('SELECT 1'))
|
| 22 |
+
result.fetchone()
|
| 23 |
+
|
| 24 |
+
# Test if migrations table exists (indicates proper setup)
|
| 25 |
+
result = db.session.execute(text(
|
| 26 |
+
"SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'schema_migrations'"
|
| 27 |
+
))
|
| 28 |
+
migrations_table_exists = result.fetchone()[0] > 0
|
| 29 |
+
|
| 30 |
+
return {
|
| 31 |
+
'status': 'healthy',
|
| 32 |
+
'connection': 'ok',
|
| 33 |
+
'migrations_table': 'exists' if migrations_table_exists else 'missing',
|
| 34 |
+
'response_time_ms': 0 # Will be calculated by caller
|
| 35 |
+
}
|
| 36 |
+
except Exception as e:
|
| 37 |
+
return {
|
| 38 |
+
'status': 'unhealthy',
|
| 39 |
+
'error': str(e),
|
| 40 |
+
'connection': 'failed'
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def check_redis():
|
| 45 |
+
"""Check Redis connectivity and basic operations."""
|
| 46 |
+
redis_url = current_app.config.get('REDIS_URL')
|
| 47 |
+
if not redis_url or redis_url == 'None':
|
| 48 |
+
return {
|
| 49 |
+
'status': 'disabled',
|
| 50 |
+
'message': 'Redis is disabled in configuration'
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
try:
|
| 54 |
+
redis_client = redis.from_url(redis_url)
|
| 55 |
+
|
| 56 |
+
# Test basic operations
|
| 57 |
+
start_time = time.time()
|
| 58 |
+
redis_client.ping()
|
| 59 |
+
response_time = (time.time() - start_time) * 1000
|
| 60 |
+
|
| 61 |
+
# Test set/get operation
|
| 62 |
+
test_key = 'health_check_test'
|
| 63 |
+
redis_client.set(test_key, 'test_value', ex=10)
|
| 64 |
+
value = redis_client.get(test_key)
|
| 65 |
+
redis_client.delete(test_key)
|
| 66 |
+
|
| 67 |
+
return {
|
| 68 |
+
'status': 'healthy',
|
| 69 |
+
'connection': 'ok',
|
| 70 |
+
'response_time_ms': round(response_time, 2),
|
| 71 |
+
'operations': 'ok' if value == b'test_value' else 'failed'
|
| 72 |
+
}
|
| 73 |
+
except Exception as e:
|
| 74 |
+
return {
|
| 75 |
+
'status': 'unhealthy',
|
| 76 |
+
'error': str(e),
|
| 77 |
+
'connection': 'failed'
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def check_groq_api():
|
| 82 |
+
"""Check Groq API configuration and basic connectivity."""
|
| 83 |
+
groq_api_key = current_app.config.get('GROQ_API_KEY')
|
| 84 |
+
|
| 85 |
+
if not groq_api_key:
|
| 86 |
+
return {
|
| 87 |
+
'status': 'unhealthy',
|
| 88 |
+
'error': 'GROQ_API_KEY not configured'
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
# Basic configuration check
|
| 92 |
+
return {
|
| 93 |
+
'status': 'configured',
|
| 94 |
+
'api_key_present': bool(groq_api_key),
|
| 95 |
+
'model': current_app.config.get('GROQ_MODEL', 'not_configured'),
|
| 96 |
+
'note': 'API connectivity not tested in health check to avoid quota usage'
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def get_system_metrics():
|
| 101 |
+
"""Get basic system metrics."""
|
| 102 |
+
try:
|
| 103 |
+
return {
|
| 104 |
+
'cpu_percent': psutil.cpu_percent(interval=1),
|
| 105 |
+
'memory_percent': psutil.virtual_memory().percent,
|
| 106 |
+
'disk_percent': psutil.disk_usage('/').percent,
|
| 107 |
+
'load_average': psutil.getloadavg()[0] if hasattr(psutil, 'getloadavg') else None
|
| 108 |
+
}
|
| 109 |
+
except Exception as e:
|
| 110 |
+
return {
|
| 111 |
+
'error': f'Failed to get system metrics: {str(e)}'
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
@health_bp.route('/')
|
| 116 |
+
@health_bp.route('/basic')
|
| 117 |
+
def basic_health():
|
| 118 |
+
"""Basic health check endpoint for load balancers."""
|
| 119 |
+
return jsonify({
|
| 120 |
+
'status': 'healthy',
|
| 121 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 122 |
+
'service': 'chat-agent',
|
| 123 |
+
'version': '1.0.0'
|
| 124 |
+
}), 200
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
@health_bp.route('/detailed')
|
| 128 |
+
def detailed_health():
|
| 129 |
+
"""Detailed health check with all dependencies."""
|
| 130 |
+
start_time = time.time()
|
| 131 |
+
|
| 132 |
+
# Check all components
|
| 133 |
+
db_start = time.time()
|
| 134 |
+
database_health = check_database()
|
| 135 |
+
database_health['response_time_ms'] = round((time.time() - db_start) * 1000, 2)
|
| 136 |
+
|
| 137 |
+
redis_health = check_redis()
|
| 138 |
+
groq_health = check_groq_api()
|
| 139 |
+
system_metrics = get_system_metrics()
|
| 140 |
+
|
| 141 |
+
# Determine overall status
|
| 142 |
+
overall_status = 'healthy'
|
| 143 |
+
if database_health['status'] == 'unhealthy':
|
| 144 |
+
overall_status = 'unhealthy'
|
| 145 |
+
elif redis_health['status'] == 'unhealthy':
|
| 146 |
+
overall_status = 'degraded' # Redis failure is not critical
|
| 147 |
+
elif groq_health['status'] == 'unhealthy':
|
| 148 |
+
overall_status = 'degraded' # Can still serve static content
|
| 149 |
+
|
| 150 |
+
response = {
|
| 151 |
+
'status': overall_status,
|
| 152 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 153 |
+
'service': 'chat-agent',
|
| 154 |
+
'version': '1.0.0',
|
| 155 |
+
'uptime_seconds': round(time.time() - start_time, 2),
|
| 156 |
+
'components': {
|
| 157 |
+
'database': database_health,
|
| 158 |
+
'redis': redis_health,
|
| 159 |
+
'groq_api': groq_health
|
| 160 |
+
},
|
| 161 |
+
'system': system_metrics,
|
| 162 |
+
'config': {
|
| 163 |
+
'environment': current_app.config.get('FLASK_ENV', 'unknown'),
|
| 164 |
+
'debug': current_app.config.get('DEBUG', False),
|
| 165 |
+
'default_language': current_app.config.get('DEFAULT_LANGUAGE', 'python')
|
| 166 |
+
}
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
# Return appropriate HTTP status code
|
| 170 |
+
status_code = 200
|
| 171 |
+
if overall_status == 'unhealthy':
|
| 172 |
+
status_code = 503
|
| 173 |
+
elif overall_status == 'degraded':
|
| 174 |
+
status_code = 200 # Still functional
|
| 175 |
+
|
| 176 |
+
return jsonify(response), status_code
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
@health_bp.route('/ready')
|
| 180 |
+
def readiness():
|
| 181 |
+
"""Readiness probe for Kubernetes/container orchestration."""
|
| 182 |
+
# Check critical dependencies only
|
| 183 |
+
db_health = check_database()
|
| 184 |
+
|
| 185 |
+
if db_health['status'] == 'healthy':
|
| 186 |
+
return jsonify({
|
| 187 |
+
'status': 'ready',
|
| 188 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 189 |
+
'database': 'connected'
|
| 190 |
+
}), 200
|
| 191 |
+
else:
|
| 192 |
+
return jsonify({
|
| 193 |
+
'status': 'not_ready',
|
| 194 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 195 |
+
'database': 'disconnected',
|
| 196 |
+
'error': db_health.get('error', 'Database check failed')
|
| 197 |
+
}), 503
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
@health_bp.route('/live')
|
| 201 |
+
def liveness():
|
| 202 |
+
"""Liveness probe for Kubernetes/container orchestration."""
|
| 203 |
+
# Simple check that the application is running
|
| 204 |
+
return jsonify({
|
| 205 |
+
'status': 'alive',
|
| 206 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 207 |
+
'service': 'chat-agent'
|
| 208 |
+
}), 200
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
@health_bp.route('/metrics')
|
| 212 |
+
def metrics():
|
| 213 |
+
"""Basic metrics endpoint for monitoring systems."""
|
| 214 |
+
system_metrics = get_system_metrics()
|
| 215 |
+
|
| 216 |
+
# Add application-specific metrics
|
| 217 |
+
app_metrics = {
|
| 218 |
+
'active_sessions': 0, # TODO: Implement session counting
|
| 219 |
+
'total_messages': 0, # TODO: Implement message counting
|
| 220 |
+
'cache_hit_rate': 0.0 # TODO: Implement cache metrics
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
return jsonify({
|
| 224 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 225 |
+
'system': system_metrics,
|
| 226 |
+
'application': app_metrics
|
| 227 |
+
}), 200
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
# Error handler for health check blueprint
|
| 231 |
+
@health_bp.errorhandler(Exception)
|
| 232 |
+
def handle_health_error(error):
|
| 233 |
+
"""Handle errors in health check endpoints."""
|
| 234 |
+
error_handler = get_error_handler()
|
| 235 |
+
if error_handler:
|
| 236 |
+
error_handler.handle_error(error, context="health_check")
|
| 237 |
+
|
| 238 |
+
return jsonify({
|
| 239 |
+
'status': 'error',
|
| 240 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 241 |
+
'error': 'Health check failed',
|
| 242 |
+
'message': str(error)
|
| 243 |
+
}), 500
|
chat_agent/api/middleware.py
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Middleware for authentication, authorization, and rate limiting."""
|
| 2 |
+
|
| 3 |
+
import logging
|
| 4 |
+
import time
|
| 5 |
+
from functools import wraps
|
| 6 |
+
from typing import Dict, Any, Optional
|
| 7 |
+
|
| 8 |
+
from flask import request, jsonify, current_app, g
|
| 9 |
+
from flask_limiter import Limiter
|
| 10 |
+
from flask_limiter.util import get_remote_address
|
| 11 |
+
import redis
|
| 12 |
+
import jwt
|
| 13 |
+
from datetime import datetime, timedelta
|
| 14 |
+
|
| 15 |
+
logger = logging.getLogger(__name__)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class AuthenticationError(Exception):
|
| 19 |
+
"""Authentication related errors."""
|
| 20 |
+
pass
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class AuthorizationError(Exception):
|
| 24 |
+
"""Authorization related errors."""
|
| 25 |
+
pass
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class RateLimitError(Exception):
|
| 29 |
+
"""Rate limiting related errors."""
|
| 30 |
+
pass
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def create_limiter(app=None):
|
| 34 |
+
"""Create and configure rate limiter."""
|
| 35 |
+
limiter = Limiter(
|
| 36 |
+
key_func=get_remote_address,
|
| 37 |
+
default_limits=["200 per day", "50 per hour"],
|
| 38 |
+
storage_uri=None # Will be set from app config
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
if app:
|
| 42 |
+
limiter.init_app(app)
|
| 43 |
+
|
| 44 |
+
return limiter
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
class SimpleAuthManager:
|
| 48 |
+
"""
|
| 49 |
+
Simple authentication manager for development/testing.
|
| 50 |
+
In production, this would be replaced with proper JWT/OAuth implementation.
|
| 51 |
+
"""
|
| 52 |
+
|
| 53 |
+
def __init__(self, redis_client: Optional[redis.Redis] = None):
|
| 54 |
+
"""Initialize auth manager."""
|
| 55 |
+
self.redis_client = redis_client
|
| 56 |
+
self.session_prefix = "auth_session:"
|
| 57 |
+
self.user_prefix = "user:"
|
| 58 |
+
|
| 59 |
+
def create_session_token(self, user_id: str, expires_in: int = 3600) -> str:
|
| 60 |
+
"""
|
| 61 |
+
Create a simple session token for a user.
|
| 62 |
+
|
| 63 |
+
Args:
|
| 64 |
+
user_id: User identifier
|
| 65 |
+
expires_in: Token expiration in seconds
|
| 66 |
+
|
| 67 |
+
Returns:
|
| 68 |
+
str: Session token
|
| 69 |
+
"""
|
| 70 |
+
try:
|
| 71 |
+
# Create a simple token (in production, use proper JWT)
|
| 72 |
+
token_data = {
|
| 73 |
+
'user_id': user_id,
|
| 74 |
+
'created_at': time.time(),
|
| 75 |
+
'expires_at': time.time() + expires_in
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
# For simplicity, use user_id as token (in production, use secure random token)
|
| 79 |
+
token = f"session_{user_id}_{int(time.time())}"
|
| 80 |
+
|
| 81 |
+
if self.redis_client:
|
| 82 |
+
# Store token in Redis
|
| 83 |
+
self.redis_client.setex(
|
| 84 |
+
f"{self.session_prefix}{token}",
|
| 85 |
+
expires_in,
|
| 86 |
+
user_id
|
| 87 |
+
)
|
| 88 |
+
|
| 89 |
+
return token
|
| 90 |
+
|
| 91 |
+
except Exception as e:
|
| 92 |
+
logger.error(f"Error creating session token: {e}")
|
| 93 |
+
raise AuthenticationError(f"Failed to create session token: {e}")
|
| 94 |
+
|
| 95 |
+
def validate_session_token(self, token: str) -> Optional[str]:
|
| 96 |
+
"""
|
| 97 |
+
Validate a session token and return user_id if valid.
|
| 98 |
+
|
| 99 |
+
Args:
|
| 100 |
+
token: Session token to validate
|
| 101 |
+
|
| 102 |
+
Returns:
|
| 103 |
+
str: User ID if token is valid, None otherwise
|
| 104 |
+
"""
|
| 105 |
+
try:
|
| 106 |
+
if not token:
|
| 107 |
+
return None
|
| 108 |
+
|
| 109 |
+
if self.redis_client:
|
| 110 |
+
# Check Redis for token
|
| 111 |
+
user_id = self.redis_client.get(f"{self.session_prefix}{token}")
|
| 112 |
+
if user_id:
|
| 113 |
+
return user_id.decode('utf-8')
|
| 114 |
+
|
| 115 |
+
# Fallback: simple token validation (for development)
|
| 116 |
+
if token.startswith('session_'):
|
| 117 |
+
parts = token.split('_')
|
| 118 |
+
if len(parts) >= 2:
|
| 119 |
+
return parts[1] # Return user_id part
|
| 120 |
+
|
| 121 |
+
return None
|
| 122 |
+
|
| 123 |
+
except Exception as e:
|
| 124 |
+
logger.error(f"Error validating session token: {e}")
|
| 125 |
+
return None
|
| 126 |
+
|
| 127 |
+
def revoke_session_token(self, token: str) -> bool:
|
| 128 |
+
"""
|
| 129 |
+
Revoke a session token.
|
| 130 |
+
|
| 131 |
+
Args:
|
| 132 |
+
token: Session token to revoke
|
| 133 |
+
|
| 134 |
+
Returns:
|
| 135 |
+
bool: True if token was revoked, False otherwise
|
| 136 |
+
"""
|
| 137 |
+
try:
|
| 138 |
+
if self.redis_client:
|
| 139 |
+
result = self.redis_client.delete(f"{self.session_prefix}{token}")
|
| 140 |
+
return result > 0
|
| 141 |
+
|
| 142 |
+
return True # For development, always return True
|
| 143 |
+
|
| 144 |
+
except Exception as e:
|
| 145 |
+
logger.error(f"Error revoking session token: {e}")
|
| 146 |
+
return False
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def require_auth(f):
|
| 150 |
+
"""
|
| 151 |
+
Authentication decorator for API endpoints.
|
| 152 |
+
Supports both header-based authentication and session tokens.
|
| 153 |
+
"""
|
| 154 |
+
@wraps(f)
|
| 155 |
+
def decorated_function(*args, **kwargs):
|
| 156 |
+
try:
|
| 157 |
+
# Check for session token first
|
| 158 |
+
auth_header = request.headers.get('Authorization')
|
| 159 |
+
if auth_header and auth_header.startswith('Bearer '):
|
| 160 |
+
token = auth_header.split(' ')[1]
|
| 161 |
+
|
| 162 |
+
# Get auth manager from app context
|
| 163 |
+
redis_client = redis.from_url(current_app.config['REDIS_URL'])
|
| 164 |
+
auth_manager = SimpleAuthManager(redis_client)
|
| 165 |
+
|
| 166 |
+
user_id = auth_manager.validate_session_token(token)
|
| 167 |
+
if user_id:
|
| 168 |
+
g.user_id = user_id
|
| 169 |
+
request.user_id = user_id
|
| 170 |
+
return f(*args, **kwargs)
|
| 171 |
+
|
| 172 |
+
# Fallback to simple header-based auth (for development)
|
| 173 |
+
user_id = request.headers.get('X-User-ID')
|
| 174 |
+
if user_id:
|
| 175 |
+
g.user_id = user_id
|
| 176 |
+
request.user_id = user_id
|
| 177 |
+
return f(*args, **kwargs)
|
| 178 |
+
|
| 179 |
+
# No valid authentication found
|
| 180 |
+
return jsonify({
|
| 181 |
+
'error': 'Authentication required',
|
| 182 |
+
'message': 'Please provide a valid Authorization header or X-User-ID header'
|
| 183 |
+
}), 401
|
| 184 |
+
|
| 185 |
+
except Exception as e:
|
| 186 |
+
logger.error(f"Authentication error: {e}")
|
| 187 |
+
return jsonify({
|
| 188 |
+
'error': 'Authentication failed',
|
| 189 |
+
'message': 'Invalid authentication credentials'
|
| 190 |
+
}), 401
|
| 191 |
+
|
| 192 |
+
return decorated_function
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def require_session_ownership(f):
|
| 196 |
+
"""
|
| 197 |
+
Authorization decorator to ensure user owns the session.
|
| 198 |
+
Must be used after require_auth.
|
| 199 |
+
"""
|
| 200 |
+
@wraps(f)
|
| 201 |
+
def decorated_function(*args, **kwargs):
|
| 202 |
+
try:
|
| 203 |
+
session_id = kwargs.get('session_id') or request.view_args.get('session_id')
|
| 204 |
+
if not session_id:
|
| 205 |
+
return jsonify({
|
| 206 |
+
'error': 'Session ID required',
|
| 207 |
+
'message': 'Session ID must be provided in the URL'
|
| 208 |
+
}), 400
|
| 209 |
+
|
| 210 |
+
user_id = getattr(g, 'user_id', None) or getattr(request, 'user_id', None)
|
| 211 |
+
if not user_id:
|
| 212 |
+
return jsonify({
|
| 213 |
+
'error': 'User not authenticated',
|
| 214 |
+
'message': 'User authentication required'
|
| 215 |
+
}), 401
|
| 216 |
+
|
| 217 |
+
# Import here to avoid circular imports
|
| 218 |
+
from ..services.session_manager import SessionManager, SessionNotFoundError
|
| 219 |
+
|
| 220 |
+
redis_client = redis.from_url(current_app.config['REDIS_URL'])
|
| 221 |
+
session_manager = SessionManager(redis_client)
|
| 222 |
+
|
| 223 |
+
try:
|
| 224 |
+
session = session_manager.get_session(session_id)
|
| 225 |
+
if session.user_id != user_id:
|
| 226 |
+
return jsonify({
|
| 227 |
+
'error': 'Access denied',
|
| 228 |
+
'message': 'You do not have permission to access this session'
|
| 229 |
+
}), 403
|
| 230 |
+
|
| 231 |
+
# Store session in request context for use in endpoint
|
| 232 |
+
g.session = session
|
| 233 |
+
request.session = session
|
| 234 |
+
|
| 235 |
+
except SessionNotFoundError:
|
| 236 |
+
return jsonify({
|
| 237 |
+
'error': 'Session not found',
|
| 238 |
+
'message': f'Session {session_id} does not exist'
|
| 239 |
+
}), 404
|
| 240 |
+
|
| 241 |
+
return f(*args, **kwargs)
|
| 242 |
+
|
| 243 |
+
except Exception as e:
|
| 244 |
+
logger.error(f"Authorization error: {e}")
|
| 245 |
+
return jsonify({
|
| 246 |
+
'error': 'Authorization failed',
|
| 247 |
+
'message': 'Failed to verify session ownership'
|
| 248 |
+
}), 500
|
| 249 |
+
|
| 250 |
+
return decorated_function
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
def validate_json_request(required_fields: list = None, optional_fields: list = None):
|
| 254 |
+
"""
|
| 255 |
+
Decorator to validate JSON request data.
|
| 256 |
+
|
| 257 |
+
Args:
|
| 258 |
+
required_fields: List of required field names
|
| 259 |
+
optional_fields: List of optional field names (for documentation)
|
| 260 |
+
"""
|
| 261 |
+
def decorator(f):
|
| 262 |
+
@wraps(f)
|
| 263 |
+
def decorated_function(*args, **kwargs):
|
| 264 |
+
if not request.is_json:
|
| 265 |
+
return jsonify({
|
| 266 |
+
'error': 'Invalid content type',
|
| 267 |
+
'message': 'Request must be JSON'
|
| 268 |
+
}), 400
|
| 269 |
+
|
| 270 |
+
try:
|
| 271 |
+
data = request.get_json()
|
| 272 |
+
except Exception as e:
|
| 273 |
+
return jsonify({
|
| 274 |
+
'error': 'Invalid JSON',
|
| 275 |
+
'message': f'Failed to parse JSON: {str(e)}'
|
| 276 |
+
}), 400
|
| 277 |
+
|
| 278 |
+
if not data:
|
| 279 |
+
return jsonify({
|
| 280 |
+
'error': 'Empty request body',
|
| 281 |
+
'message': 'Request body cannot be empty'
|
| 282 |
+
}), 400
|
| 283 |
+
|
| 284 |
+
if required_fields:
|
| 285 |
+
missing_fields = [field for field in required_fields if field not in data]
|
| 286 |
+
if missing_fields:
|
| 287 |
+
return jsonify({
|
| 288 |
+
'error': 'Missing required fields',
|
| 289 |
+
'message': f'Required fields: {", ".join(missing_fields)}',
|
| 290 |
+
'missing_fields': missing_fields
|
| 291 |
+
}), 400
|
| 292 |
+
|
| 293 |
+
# Store validated data in request context
|
| 294 |
+
g.json_data = data
|
| 295 |
+
request.json_data = data
|
| 296 |
+
|
| 297 |
+
return f(*args, **kwargs)
|
| 298 |
+
|
| 299 |
+
return decorated_function
|
| 300 |
+
return decorator
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
def handle_rate_limit_exceeded(e):
|
| 304 |
+
"""Handle rate limit exceeded errors."""
|
| 305 |
+
return jsonify({
|
| 306 |
+
'error': 'Rate limit exceeded',
|
| 307 |
+
'message': 'Too many requests. Please try again later.',
|
| 308 |
+
'retry_after': getattr(e, 'retry_after', None)
|
| 309 |
+
}), 429
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
def setup_error_handlers(app):
|
| 313 |
+
"""Setup error handlers for the application."""
|
| 314 |
+
|
| 315 |
+
@app.errorhandler(AuthenticationError)
|
| 316 |
+
def handle_auth_error(error):
|
| 317 |
+
return jsonify({
|
| 318 |
+
'error': 'Authentication failed',
|
| 319 |
+
'message': str(error)
|
| 320 |
+
}), 401
|
| 321 |
+
|
| 322 |
+
@app.errorhandler(AuthorizationError)
|
| 323 |
+
def handle_authz_error(error):
|
| 324 |
+
return jsonify({
|
| 325 |
+
'error': 'Authorization failed',
|
| 326 |
+
'message': str(error)
|
| 327 |
+
}), 403
|
| 328 |
+
|
| 329 |
+
@app.errorhandler(429)
|
| 330 |
+
def handle_rate_limit(error):
|
| 331 |
+
return handle_rate_limit_exceeded(error)
|
| 332 |
+
|
| 333 |
+
@app.errorhandler(400)
|
| 334 |
+
def handle_bad_request(error):
|
| 335 |
+
return jsonify({
|
| 336 |
+
'error': 'Bad request',
|
| 337 |
+
'message': 'The request could not be understood by the server'
|
| 338 |
+
}), 400
|
| 339 |
+
|
| 340 |
+
@app.errorhandler(404)
|
| 341 |
+
def handle_not_found(error):
|
| 342 |
+
return jsonify({
|
| 343 |
+
'error': 'Not found',
|
| 344 |
+
'message': 'The requested resource was not found'
|
| 345 |
+
}), 404
|
| 346 |
+
|
| 347 |
+
@app.errorhandler(500)
|
| 348 |
+
def handle_internal_error(error):
|
| 349 |
+
logger.error(f"Internal server error: {error}")
|
| 350 |
+
return jsonify({
|
| 351 |
+
'error': 'Internal server error',
|
| 352 |
+
'message': 'An unexpected error occurred'
|
| 353 |
+
}), 500
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
class RequestLoggingMiddleware:
|
| 357 |
+
"""Middleware for logging API requests."""
|
| 358 |
+
|
| 359 |
+
def __init__(self, app=None):
|
| 360 |
+
self.app = app
|
| 361 |
+
if app:
|
| 362 |
+
self.init_app(app)
|
| 363 |
+
|
| 364 |
+
def init_app(self, app):
|
| 365 |
+
"""Initialize the middleware with the Flask app."""
|
| 366 |
+
app.before_request(self.log_request)
|
| 367 |
+
app.after_request(self.log_response)
|
| 368 |
+
|
| 369 |
+
def log_request(self):
|
| 370 |
+
"""Log incoming requests."""
|
| 371 |
+
if request.endpoint and not request.endpoint.startswith('static'):
|
| 372 |
+
logger.info(f"API Request: {request.method} {request.path} from {request.remote_addr}")
|
| 373 |
+
|
| 374 |
+
# Log request data for debugging (be careful with sensitive data)
|
| 375 |
+
if request.is_json and current_app.debug:
|
| 376 |
+
try:
|
| 377 |
+
data = request.get_json()
|
| 378 |
+
# Remove sensitive fields before logging
|
| 379 |
+
safe_data = {k: v for k, v in data.items() if k not in ['password', 'token', 'secret']}
|
| 380 |
+
logger.debug(f"Request data: {safe_data}")
|
| 381 |
+
except:
|
| 382 |
+
pass
|
| 383 |
+
|
| 384 |
+
def log_response(self, response):
|
| 385 |
+
"""Log outgoing responses."""
|
| 386 |
+
if request.endpoint and not request.endpoint.startswith('static'):
|
| 387 |
+
logger.info(f"API Response: {response.status_code} for {request.method} {request.path}")
|
| 388 |
+
|
| 389 |
+
return response
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
def create_auth_manager(redis_client: redis.Redis) -> SimpleAuthManager:
|
| 393 |
+
"""
|
| 394 |
+
Factory function to create an authentication manager.
|
| 395 |
+
|
| 396 |
+
Args:
|
| 397 |
+
redis_client: Redis client instance
|
| 398 |
+
|
| 399 |
+
Returns:
|
| 400 |
+
SimpleAuthManager: Configured auth manager instance
|
| 401 |
+
"""
|
| 402 |
+
return SimpleAuthManager(redis_client)
|
chat_agent/api/performance_routes.py
ADDED
|
@@ -0,0 +1,441 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Performance monitoring API routes.
|
| 3 |
+
|
| 4 |
+
This module provides endpoints for monitoring system performance,
|
| 5 |
+
connection pool status, cache statistics, and other performance metrics.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import logging
|
| 9 |
+
from datetime import datetime
|
| 10 |
+
from typing import Dict, Any
|
| 11 |
+
|
| 12 |
+
from flask import Blueprint, jsonify, current_app
|
| 13 |
+
from flask_limiter import Limiter
|
| 14 |
+
|
| 15 |
+
from ..utils.connection_pool import get_connection_pool_manager
|
| 16 |
+
from ..services.cache_service import get_cache_service
|
| 17 |
+
from .middleware import require_auth, create_limiter
|
| 18 |
+
from ..utils.response_optimization import cache_response, compress_response
|
| 19 |
+
|
| 20 |
+
logger = logging.getLogger(__name__)
|
| 21 |
+
|
| 22 |
+
# Create blueprint
|
| 23 |
+
performance_bp = Blueprint('performance', __name__, url_prefix='/api/v1/performance')
|
| 24 |
+
|
| 25 |
+
# Initialize rate limiter
|
| 26 |
+
limiter = create_limiter()
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@performance_bp.route('/status', methods=['GET'])
|
| 30 |
+
@limiter.limit("30 per minute")
|
| 31 |
+
@cache_response(max_age=60, cache_type='private') # Cache for 1 minute
|
| 32 |
+
@compress_response
|
| 33 |
+
def get_performance_status():
|
| 34 |
+
"""
|
| 35 |
+
Get overall system performance status.
|
| 36 |
+
|
| 37 |
+
Returns:
|
| 38 |
+
JSON response with performance metrics
|
| 39 |
+
"""
|
| 40 |
+
try:
|
| 41 |
+
status = {
|
| 42 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 43 |
+
'status': 'healthy',
|
| 44 |
+
'services': {}
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
# Database connection pool status
|
| 48 |
+
connection_pool_manager = get_connection_pool_manager()
|
| 49 |
+
if connection_pool_manager:
|
| 50 |
+
pool_status = connection_pool_manager.get_status()
|
| 51 |
+
status['services']['database'] = {
|
| 52 |
+
'status': 'connected',
|
| 53 |
+
'pool_stats': pool_status['database']
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
if pool_status['redis']:
|
| 57 |
+
status['services']['redis'] = {
|
| 58 |
+
'status': 'connected' if pool_status['redis']['healthy'] else 'unhealthy',
|
| 59 |
+
'pool_stats': pool_status['redis']
|
| 60 |
+
}
|
| 61 |
+
else:
|
| 62 |
+
status['services']['redis'] = {
|
| 63 |
+
'status': 'disabled'
|
| 64 |
+
}
|
| 65 |
+
else:
|
| 66 |
+
status['services']['database'] = {'status': 'no_pool_manager'}
|
| 67 |
+
status['services']['redis'] = {'status': 'no_pool_manager'}
|
| 68 |
+
|
| 69 |
+
# Cache service status
|
| 70 |
+
cache_service = get_cache_service()
|
| 71 |
+
if cache_service:
|
| 72 |
+
cache_stats = cache_service.get_cache_stats()
|
| 73 |
+
status['services']['cache'] = {
|
| 74 |
+
'status': 'enabled' if cache_stats['redis_enabled'] else 'disabled',
|
| 75 |
+
'stats': cache_stats
|
| 76 |
+
}
|
| 77 |
+
else:
|
| 78 |
+
status['services']['cache'] = {'status': 'not_initialized'}
|
| 79 |
+
|
| 80 |
+
# Application configuration
|
| 81 |
+
status['configuration'] = {
|
| 82 |
+
'compression_enabled': current_app.config.get('ENABLE_COMPRESSION', False),
|
| 83 |
+
'caching_enabled': current_app.config.get('ENABLE_CACHING', False),
|
| 84 |
+
'performance_monitoring': current_app.config.get('ENABLE_PERFORMANCE_MONITORING', False),
|
| 85 |
+
'db_pool_size': current_app.config.get('DB_POOL_SIZE', 10),
|
| 86 |
+
'redis_max_connections': current_app.config.get('REDIS_MAX_CONNECTIONS', 20)
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
return jsonify(status)
|
| 90 |
+
|
| 91 |
+
except Exception as e:
|
| 92 |
+
logger.error(f"Error getting performance status: {e}")
|
| 93 |
+
return jsonify({
|
| 94 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 95 |
+
'status': 'error',
|
| 96 |
+
'error': str(e)
|
| 97 |
+
}), 500
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
@performance_bp.route('/database', methods=['GET'])
|
| 101 |
+
@limiter.limit("20 per minute")
|
| 102 |
+
@require_auth
|
| 103 |
+
@cache_response(max_age=30, cache_type='private')
|
| 104 |
+
@compress_response
|
| 105 |
+
def get_database_performance():
|
| 106 |
+
"""
|
| 107 |
+
Get database performance metrics.
|
| 108 |
+
|
| 109 |
+
Returns:
|
| 110 |
+
JSON response with database performance data
|
| 111 |
+
"""
|
| 112 |
+
try:
|
| 113 |
+
connection_pool_manager = get_connection_pool_manager()
|
| 114 |
+
|
| 115 |
+
if not connection_pool_manager:
|
| 116 |
+
return jsonify({
|
| 117 |
+
'error': 'Connection pool manager not available'
|
| 118 |
+
}), 503
|
| 119 |
+
|
| 120 |
+
# Get database pool status
|
| 121 |
+
pool_status = connection_pool_manager.get_status()
|
| 122 |
+
database_stats = pool_status['database']
|
| 123 |
+
|
| 124 |
+
# Calculate pool utilization
|
| 125 |
+
total_connections = database_stats['pool_size'] + database_stats['overflow']
|
| 126 |
+
active_connections = database_stats['checked_out']
|
| 127 |
+
utilization = (active_connections / total_connections * 100) if total_connections > 0 else 0
|
| 128 |
+
|
| 129 |
+
response_data = {
|
| 130 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 131 |
+
'pool_stats': database_stats,
|
| 132 |
+
'utilization': {
|
| 133 |
+
'active_connections': active_connections,
|
| 134 |
+
'total_connections': total_connections,
|
| 135 |
+
'utilization_percent': round(utilization, 2)
|
| 136 |
+
},
|
| 137 |
+
'health': {
|
| 138 |
+
'status': 'healthy' if utilization < 80 else 'warning' if utilization < 95 else 'critical',
|
| 139 |
+
'invalid_connections': database_stats['invalid']
|
| 140 |
+
}
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
return jsonify(response_data)
|
| 144 |
+
|
| 145 |
+
except Exception as e:
|
| 146 |
+
logger.error(f"Error getting database performance: {e}")
|
| 147 |
+
return jsonify({
|
| 148 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 149 |
+
'error': str(e)
|
| 150 |
+
}), 500
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
@performance_bp.route('/redis', methods=['GET'])
|
| 154 |
+
@limiter.limit("20 per minute")
|
| 155 |
+
@require_auth
|
| 156 |
+
@cache_response(max_age=30, cache_type='private')
|
| 157 |
+
@compress_response
|
| 158 |
+
def get_redis_performance():
|
| 159 |
+
"""
|
| 160 |
+
Get Redis performance metrics.
|
| 161 |
+
|
| 162 |
+
Returns:
|
| 163 |
+
JSON response with Redis performance data
|
| 164 |
+
"""
|
| 165 |
+
try:
|
| 166 |
+
connection_pool_manager = get_connection_pool_manager()
|
| 167 |
+
|
| 168 |
+
if not connection_pool_manager:
|
| 169 |
+
return jsonify({
|
| 170 |
+
'error': 'Connection pool manager not available'
|
| 171 |
+
}), 503
|
| 172 |
+
|
| 173 |
+
redis_client = connection_pool_manager.get_redis_client()
|
| 174 |
+
|
| 175 |
+
if not redis_client:
|
| 176 |
+
return jsonify({
|
| 177 |
+
'status': 'disabled',
|
| 178 |
+
'message': 'Redis is not configured'
|
| 179 |
+
})
|
| 180 |
+
|
| 181 |
+
# Get Redis pool status
|
| 182 |
+
pool_status = connection_pool_manager.get_status()
|
| 183 |
+
redis_stats = pool_status['redis']
|
| 184 |
+
|
| 185 |
+
# Get Redis server info
|
| 186 |
+
try:
|
| 187 |
+
redis_info = redis_client.info()
|
| 188 |
+
server_stats = {
|
| 189 |
+
'version': redis_info.get('redis_version', 'unknown'),
|
| 190 |
+
'uptime_seconds': redis_info.get('uptime_in_seconds', 0),
|
| 191 |
+
'connected_clients': redis_info.get('connected_clients', 0),
|
| 192 |
+
'used_memory': redis_info.get('used_memory_human', 'unknown'),
|
| 193 |
+
'keyspace_hits': redis_info.get('keyspace_hits', 0),
|
| 194 |
+
'keyspace_misses': redis_info.get('keyspace_misses', 0),
|
| 195 |
+
'total_commands_processed': redis_info.get('total_commands_processed', 0)
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
# Calculate hit rate
|
| 199 |
+
total_keyspace_ops = server_stats['keyspace_hits'] + server_stats['keyspace_misses']
|
| 200 |
+
hit_rate = (server_stats['keyspace_hits'] / total_keyspace_ops * 100) if total_keyspace_ops > 0 else 0
|
| 201 |
+
|
| 202 |
+
except Exception as e:
|
| 203 |
+
logger.warning(f"Failed to get Redis server info: {e}")
|
| 204 |
+
server_stats = {'error': 'Failed to get server info'}
|
| 205 |
+
hit_rate = 0
|
| 206 |
+
|
| 207 |
+
response_data = {
|
| 208 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 209 |
+
'pool_stats': redis_stats,
|
| 210 |
+
'server_stats': server_stats,
|
| 211 |
+
'performance': {
|
| 212 |
+
'hit_rate_percent': round(hit_rate, 2),
|
| 213 |
+
'health_status': redis_stats.get('healthy', False)
|
| 214 |
+
}
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
return jsonify(response_data)
|
| 218 |
+
|
| 219 |
+
except Exception as e:
|
| 220 |
+
logger.error(f"Error getting Redis performance: {e}")
|
| 221 |
+
return jsonify({
|
| 222 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 223 |
+
'error': str(e)
|
| 224 |
+
}), 500
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
@performance_bp.route('/cache', methods=['GET'])
|
| 228 |
+
@limiter.limit("20 per minute")
|
| 229 |
+
@require_auth
|
| 230 |
+
@cache_response(max_age=60, cache_type='private')
|
| 231 |
+
@compress_response
|
| 232 |
+
def get_cache_performance():
|
| 233 |
+
"""
|
| 234 |
+
Get cache performance metrics.
|
| 235 |
+
|
| 236 |
+
Returns:
|
| 237 |
+
JSON response with cache performance data
|
| 238 |
+
"""
|
| 239 |
+
try:
|
| 240 |
+
cache_service = get_cache_service()
|
| 241 |
+
|
| 242 |
+
if not cache_service:
|
| 243 |
+
return jsonify({
|
| 244 |
+
'status': 'not_initialized',
|
| 245 |
+
'message': 'Cache service not initialized'
|
| 246 |
+
})
|
| 247 |
+
|
| 248 |
+
cache_stats = cache_service.get_cache_stats()
|
| 249 |
+
|
| 250 |
+
# Add performance analysis
|
| 251 |
+
performance_analysis = {
|
| 252 |
+
'efficiency': 'excellent' if cache_stats['hit_rate_percent'] >= 80 else
|
| 253 |
+
'good' if cache_stats['hit_rate_percent'] >= 60 else
|
| 254 |
+
'fair' if cache_stats['hit_rate_percent'] >= 40 else 'poor',
|
| 255 |
+
'recommendations': []
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
if cache_stats['hit_rate_percent'] < 60:
|
| 259 |
+
performance_analysis['recommendations'].append('Consider increasing cache TTL')
|
| 260 |
+
performance_analysis['recommendations'].append('Review cache key strategies')
|
| 261 |
+
|
| 262 |
+
if cache_stats['cache_errors'] > 0:
|
| 263 |
+
performance_analysis['recommendations'].append('Investigate cache errors')
|
| 264 |
+
|
| 265 |
+
response_data = {
|
| 266 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 267 |
+
'cache_stats': cache_stats,
|
| 268 |
+
'performance_analysis': performance_analysis
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
return jsonify(response_data)
|
| 272 |
+
|
| 273 |
+
except Exception as e:
|
| 274 |
+
logger.error(f"Error getting cache performance: {e}")
|
| 275 |
+
return jsonify({
|
| 276 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 277 |
+
'error': str(e)
|
| 278 |
+
}), 500
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
@performance_bp.route('/metrics', methods=['GET'])
|
| 282 |
+
@limiter.limit("10 per minute")
|
| 283 |
+
@require_auth
|
| 284 |
+
@cache_response(max_age=120, cache_type='private') # Cache for 2 minutes
|
| 285 |
+
@compress_response
|
| 286 |
+
def get_comprehensive_metrics():
|
| 287 |
+
"""
|
| 288 |
+
Get comprehensive performance metrics.
|
| 289 |
+
|
| 290 |
+
Returns:
|
| 291 |
+
JSON response with all performance metrics
|
| 292 |
+
"""
|
| 293 |
+
try:
|
| 294 |
+
metrics = {
|
| 295 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 296 |
+
'system': {},
|
| 297 |
+
'database': {},
|
| 298 |
+
'redis': {},
|
| 299 |
+
'cache': {},
|
| 300 |
+
'application': {}
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
# System metrics
|
| 304 |
+
try:
|
| 305 |
+
import psutil
|
| 306 |
+
import os
|
| 307 |
+
|
| 308 |
+
process = psutil.Process(os.getpid())
|
| 309 |
+
cpu_percent = process.cpu_percent()
|
| 310 |
+
memory_info = process.memory_info()
|
| 311 |
+
|
| 312 |
+
metrics['system'] = {
|
| 313 |
+
'cpu_percent': cpu_percent,
|
| 314 |
+
'memory_rss_mb': round(memory_info.rss / 1024 / 1024, 2),
|
| 315 |
+
'memory_vms_mb': round(memory_info.vms / 1024 / 1024, 2),
|
| 316 |
+
'num_threads': process.num_threads(),
|
| 317 |
+
'num_fds': process.num_fds() if hasattr(process, 'num_fds') else 'N/A'
|
| 318 |
+
}
|
| 319 |
+
except ImportError:
|
| 320 |
+
metrics['system'] = {'error': 'psutil not available'}
|
| 321 |
+
except Exception as e:
|
| 322 |
+
metrics['system'] = {'error': str(e)}
|
| 323 |
+
|
| 324 |
+
# Database metrics
|
| 325 |
+
connection_pool_manager = get_connection_pool_manager()
|
| 326 |
+
if connection_pool_manager:
|
| 327 |
+
pool_status = connection_pool_manager.get_status()
|
| 328 |
+
metrics['database'] = pool_status['database']
|
| 329 |
+
metrics['redis'] = pool_status['redis'] or {'status': 'disabled'}
|
| 330 |
+
|
| 331 |
+
# Cache metrics
|
| 332 |
+
cache_service = get_cache_service()
|
| 333 |
+
if cache_service:
|
| 334 |
+
metrics['cache'] = cache_service.get_cache_stats()
|
| 335 |
+
|
| 336 |
+
# Application metrics
|
| 337 |
+
metrics['application'] = {
|
| 338 |
+
'config': {
|
| 339 |
+
'compression_enabled': current_app.config.get('ENABLE_COMPRESSION', False),
|
| 340 |
+
'caching_enabled': current_app.config.get('ENABLE_CACHING', False),
|
| 341 |
+
'debug_mode': current_app.config.get('DEBUG', False)
|
| 342 |
+
},
|
| 343 |
+
'flask': {
|
| 344 |
+
'testing': current_app.testing,
|
| 345 |
+
'debug': current_app.debug
|
| 346 |
+
}
|
| 347 |
+
}
|
| 348 |
+
|
| 349 |
+
return jsonify(metrics)
|
| 350 |
+
|
| 351 |
+
except Exception as e:
|
| 352 |
+
logger.error(f"Error getting comprehensive metrics: {e}")
|
| 353 |
+
return jsonify({
|
| 354 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 355 |
+
'error': str(e)
|
| 356 |
+
}), 500
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
@performance_bp.route('/health', methods=['GET'])
|
| 360 |
+
@limiter.limit("60 per minute")
|
| 361 |
+
def performance_health_check():
|
| 362 |
+
"""
|
| 363 |
+
Quick health check for performance monitoring.
|
| 364 |
+
|
| 365 |
+
Returns:
|
| 366 |
+
JSON response with health status
|
| 367 |
+
"""
|
| 368 |
+
try:
|
| 369 |
+
health_status = {
|
| 370 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 371 |
+
'status': 'healthy',
|
| 372 |
+
'checks': {}
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
# Database health
|
| 376 |
+
try:
|
| 377 |
+
from sqlalchemy import text
|
| 378 |
+
from ..models.base import db
|
| 379 |
+
db.session.execute(text('SELECT 1'))
|
| 380 |
+
health_status['checks']['database'] = 'healthy'
|
| 381 |
+
except Exception as e:
|
| 382 |
+
health_status['checks']['database'] = f'unhealthy: {str(e)}'
|
| 383 |
+
health_status['status'] = 'degraded'
|
| 384 |
+
|
| 385 |
+
# Redis health
|
| 386 |
+
connection_pool_manager = get_connection_pool_manager()
|
| 387 |
+
if connection_pool_manager:
|
| 388 |
+
redis_client = connection_pool_manager.get_redis_client()
|
| 389 |
+
if redis_client:
|
| 390 |
+
try:
|
| 391 |
+
redis_client.ping()
|
| 392 |
+
health_status['checks']['redis'] = 'healthy'
|
| 393 |
+
except Exception as e:
|
| 394 |
+
health_status['checks']['redis'] = f'unhealthy: {str(e)}'
|
| 395 |
+
health_status['status'] = 'degraded'
|
| 396 |
+
else:
|
| 397 |
+
health_status['checks']['redis'] = 'disabled'
|
| 398 |
+
else:
|
| 399 |
+
health_status['checks']['redis'] = 'no_pool_manager'
|
| 400 |
+
|
| 401 |
+
# Cache service health
|
| 402 |
+
cache_service = get_cache_service()
|
| 403 |
+
if cache_service:
|
| 404 |
+
cache_stats = cache_service.get_cache_stats()
|
| 405 |
+
if cache_stats['cache_errors'] > 100: # Arbitrary threshold
|
| 406 |
+
health_status['checks']['cache'] = 'degraded'
|
| 407 |
+
health_status['status'] = 'degraded'
|
| 408 |
+
else:
|
| 409 |
+
health_status['checks']['cache'] = 'healthy'
|
| 410 |
+
else:
|
| 411 |
+
health_status['checks']['cache'] = 'not_initialized'
|
| 412 |
+
|
| 413 |
+
return jsonify(health_status)
|
| 414 |
+
|
| 415 |
+
except Exception as e:
|
| 416 |
+
logger.error(f"Performance health check failed: {e}")
|
| 417 |
+
return jsonify({
|
| 418 |
+
'timestamp': datetime.utcnow().isoformat(),
|
| 419 |
+
'status': 'unhealthy',
|
| 420 |
+
'error': str(e)
|
| 421 |
+
}), 503
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
# Error handlers
|
| 425 |
+
@performance_bp.errorhandler(429)
|
| 426 |
+
def handle_rate_limit_exceeded(error):
|
| 427 |
+
"""Handle rate limit exceeded errors."""
|
| 428 |
+
return jsonify({
|
| 429 |
+
'error': 'Rate limit exceeded',
|
| 430 |
+
'message': 'Too many requests to performance endpoints. Please try again later.'
|
| 431 |
+
}), 429
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
@performance_bp.errorhandler(500)
|
| 435 |
+
def handle_internal_error(error):
|
| 436 |
+
"""Handle internal server errors."""
|
| 437 |
+
logger.error(f"Performance endpoint internal error: {error}")
|
| 438 |
+
return jsonify({
|
| 439 |
+
'error': 'Internal server error',
|
| 440 |
+
'timestamp': datetime.utcnow().isoformat()
|
| 441 |
+
}), 500
|
chat_agent/models/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Models package for the chat agent application."""
|
| 2 |
+
|
| 3 |
+
from .base import db, BaseModel
|
| 4 |
+
from .message import Message
|
| 5 |
+
from .chat_session import ChatSession
|
| 6 |
+
from .language_context import LanguageContext
|
| 7 |
+
|
| 8 |
+
# Export all models and database instance
|
| 9 |
+
__all__ = [
|
| 10 |
+
'db',
|
| 11 |
+
'BaseModel',
|
| 12 |
+
'Message',
|
| 13 |
+
'ChatSession',
|
| 14 |
+
'LanguageContext'
|
| 15 |
+
]
|
chat_agent/models/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (474 Bytes). View file
|
|
|
chat_agent/models/__pycache__/base.cpython-312.pyc
ADDED
|
Binary file (3.24 kB). View file
|
|
|
chat_agent/models/__pycache__/chat_session.cpython-312.pyc
ADDED
|
Binary file (6.9 kB). View file
|
|
|
chat_agent/models/__pycache__/language_context.cpython-312.pyc
ADDED
|
Binary file (13.2 kB). View file
|
|
|
chat_agent/models/__pycache__/message.cpython-312.pyc
ADDED
|
Binary file (3.41 kB). View file
|
|
|
chat_agent/models/base.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Base model classes and database setup for the chat agent."""
|
| 2 |
+
|
| 3 |
+
from datetime import datetime
|
| 4 |
+
from uuid import uuid4
|
| 5 |
+
from sqlalchemy import Column, DateTime, String, TypeDecorator
|
| 6 |
+
from sqlalchemy.dialects.postgresql import UUID
|
| 7 |
+
from sqlalchemy.ext.declarative import declarative_base
|
| 8 |
+
from sqlalchemy.orm import sessionmaker
|
| 9 |
+
from flask_sqlalchemy import SQLAlchemy
|
| 10 |
+
|
| 11 |
+
# Create SQLAlchemy instance
|
| 12 |
+
db = SQLAlchemy()
|
| 13 |
+
|
| 14 |
+
# Custom UUID type that works with both PostgreSQL and SQLite
|
| 15 |
+
class GUID(TypeDecorator):
|
| 16 |
+
"""Platform-independent GUID type.
|
| 17 |
+
Uses PostgreSQL's UUID type, otherwise uses String(36).
|
| 18 |
+
"""
|
| 19 |
+
impl = String
|
| 20 |
+
cache_ok = True
|
| 21 |
+
|
| 22 |
+
def load_dialect_impl(self, dialect):
|
| 23 |
+
if dialect.name == 'postgresql':
|
| 24 |
+
return dialect.type_descriptor(UUID(as_uuid=True))
|
| 25 |
+
else:
|
| 26 |
+
return dialect.type_descriptor(String(36))
|
| 27 |
+
|
| 28 |
+
def process_bind_param(self, value, dialect):
|
| 29 |
+
if value is None:
|
| 30 |
+
return value
|
| 31 |
+
elif dialect.name == 'postgresql':
|
| 32 |
+
return value
|
| 33 |
+
else:
|
| 34 |
+
return str(value)
|
| 35 |
+
|
| 36 |
+
def process_result_value(self, value, dialect):
|
| 37 |
+
if value is None:
|
| 38 |
+
return value
|
| 39 |
+
else:
|
| 40 |
+
return value
|
| 41 |
+
|
| 42 |
+
# Base model class with common fields
|
| 43 |
+
class BaseModel(db.Model):
|
| 44 |
+
"""Base model class with common fields and methods."""
|
| 45 |
+
|
| 46 |
+
__abstract__ = True
|
| 47 |
+
|
| 48 |
+
id = Column(GUID(), primary_key=True, default=lambda: str(uuid4()))
|
| 49 |
+
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
| 50 |
+
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
| 51 |
+
|
| 52 |
+
def to_dict(self):
|
| 53 |
+
"""Convert model instance to dictionary."""
|
| 54 |
+
return {
|
| 55 |
+
column.name: getattr(self, column.name)
|
| 56 |
+
for column in self.__table__.columns
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
def __repr__(self):
|
| 60 |
+
"""String representation of the model."""
|
| 61 |
+
return f"<{self.__class__.__name__}(id={self.id})>"
|
chat_agent/models/chat_session.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""ChatSession model for managing user chat sessions."""
|
| 2 |
+
|
| 3 |
+
from datetime import datetime, timedelta
|
| 4 |
+
from sqlalchemy import Column, String, DateTime, Integer, Boolean, JSON
|
| 5 |
+
from sqlalchemy.orm import relationship
|
| 6 |
+
from .base import BaseModel, db, GUID
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class ChatSession(BaseModel):
|
| 10 |
+
"""Model for managing user chat sessions."""
|
| 11 |
+
|
| 12 |
+
__tablename__ = 'chat_sessions'
|
| 13 |
+
|
| 14 |
+
# Core session fields
|
| 15 |
+
user_id = Column(GUID(), nullable=False, index=True)
|
| 16 |
+
language = Column(String(50), nullable=False, default='python')
|
| 17 |
+
|
| 18 |
+
# Activity tracking
|
| 19 |
+
last_active = Column(DateTime, default=datetime.utcnow, nullable=False, index=True)
|
| 20 |
+
message_count = Column(Integer, default=0, nullable=False)
|
| 21 |
+
is_active = Column(Boolean, default=True, nullable=False, index=True)
|
| 22 |
+
|
| 23 |
+
# Session metadata
|
| 24 |
+
session_metadata = Column(JSON, default=dict) # Additional session context, preferences, etc.
|
| 25 |
+
|
| 26 |
+
# Relationships
|
| 27 |
+
messages = relationship("Message", back_populates="session", cascade="all, delete-orphan")
|
| 28 |
+
language_context = relationship("LanguageContext", back_populates="session", uselist=False, cascade="all, delete-orphan")
|
| 29 |
+
|
| 30 |
+
def __init__(self, user_id, language='python', session_metadata=None):
|
| 31 |
+
"""Initialize a new chat session."""
|
| 32 |
+
super().__init__()
|
| 33 |
+
self.user_id = user_id
|
| 34 |
+
self.language = language
|
| 35 |
+
self.last_active = datetime.utcnow()
|
| 36 |
+
self.message_count = 0
|
| 37 |
+
self.is_active = True
|
| 38 |
+
self.session_metadata = session_metadata or {}
|
| 39 |
+
|
| 40 |
+
def update_activity(self):
|
| 41 |
+
"""Update the last active timestamp."""
|
| 42 |
+
self.last_active = datetime.utcnow()
|
| 43 |
+
db.session.commit()
|
| 44 |
+
|
| 45 |
+
def increment_message_count(self):
|
| 46 |
+
"""Increment the message count for this session."""
|
| 47 |
+
self.message_count += 1
|
| 48 |
+
self.update_activity()
|
| 49 |
+
|
| 50 |
+
def set_language(self, language):
|
| 51 |
+
"""Set the programming language for this session."""
|
| 52 |
+
self.language = language
|
| 53 |
+
self.update_activity()
|
| 54 |
+
|
| 55 |
+
def deactivate(self):
|
| 56 |
+
"""Mark the session as inactive."""
|
| 57 |
+
self.is_active = False
|
| 58 |
+
db.session.commit()
|
| 59 |
+
|
| 60 |
+
def is_expired(self, timeout_seconds=3600):
|
| 61 |
+
"""Check if the session has expired based on last activity."""
|
| 62 |
+
if not self.last_active:
|
| 63 |
+
return True
|
| 64 |
+
|
| 65 |
+
expiry_time = self.last_active + timedelta(seconds=timeout_seconds)
|
| 66 |
+
return datetime.utcnow() > expiry_time
|
| 67 |
+
|
| 68 |
+
def get_recent_messages(self, limit=10):
|
| 69 |
+
"""Get recent messages for this session."""
|
| 70 |
+
# Import here to avoid circular imports
|
| 71 |
+
from .message import Message
|
| 72 |
+
return (db.session.query(Message)
|
| 73 |
+
.filter(Message.session_id == self.id)
|
| 74 |
+
.order_by(Message.timestamp.desc())
|
| 75 |
+
.limit(limit)
|
| 76 |
+
.all())
|
| 77 |
+
|
| 78 |
+
def to_dict(self):
|
| 79 |
+
"""Convert session to dictionary with formatted timestamps."""
|
| 80 |
+
data = super().to_dict()
|
| 81 |
+
# Format timestamps as ISO strings for JSON serialization
|
| 82 |
+
if self.last_active:
|
| 83 |
+
data['last_active'] = self.last_active.isoformat()
|
| 84 |
+
return data
|
| 85 |
+
|
| 86 |
+
@classmethod
|
| 87 |
+
def create_session(cls, user_id, language='python', session_metadata=None):
|
| 88 |
+
"""Create a new chat session."""
|
| 89 |
+
session = cls(user_id=user_id, language=language, session_metadata=session_metadata)
|
| 90 |
+
db.session.add(session)
|
| 91 |
+
db.session.commit()
|
| 92 |
+
return session
|
| 93 |
+
|
| 94 |
+
@classmethod
|
| 95 |
+
def get_active_sessions(cls, user_id=None):
|
| 96 |
+
"""Get all active sessions, optionally filtered by user."""
|
| 97 |
+
query = db.session.query(cls).filter(cls.is_active == True)
|
| 98 |
+
if user_id:
|
| 99 |
+
query = query.filter(cls.user_id == user_id)
|
| 100 |
+
return query.all()
|
| 101 |
+
|
| 102 |
+
@classmethod
|
| 103 |
+
def cleanup_expired_sessions(cls, timeout_seconds=3600):
|
| 104 |
+
"""Clean up expired sessions."""
|
| 105 |
+
cutoff_time = datetime.utcnow() - timedelta(seconds=timeout_seconds)
|
| 106 |
+
expired_sessions = (db.session.query(cls)
|
| 107 |
+
.filter(cls.last_active < cutoff_time)
|
| 108 |
+
.filter(cls.is_active == True)
|
| 109 |
+
.all())
|
| 110 |
+
|
| 111 |
+
for session in expired_sessions:
|
| 112 |
+
session.deactivate()
|
| 113 |
+
|
| 114 |
+
return len(expired_sessions)
|
| 115 |
+
|
| 116 |
+
def __repr__(self):
|
| 117 |
+
"""String representation of the session."""
|
| 118 |
+
return f"<ChatSession(id={self.id}, user_id={self.user_id}, language={self.language}, active={self.is_active})>"
|
chat_agent/models/language_context.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""LanguageContext model for session-specific language settings."""
|
| 2 |
+
|
| 3 |
+
from datetime import datetime
|
| 4 |
+
from sqlalchemy import Column, String, Text, DateTime
|
| 5 |
+
from sqlalchemy import ForeignKey
|
| 6 |
+
from sqlalchemy.orm import relationship
|
| 7 |
+
from .base import BaseModel, db, GUID
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class LanguageContext(BaseModel):
|
| 11 |
+
"""Model for storing session-specific language settings and context."""
|
| 12 |
+
|
| 13 |
+
__tablename__ = 'language_contexts'
|
| 14 |
+
|
| 15 |
+
# Core context fields
|
| 16 |
+
session_id = Column(GUID(), ForeignKey('chat_sessions.id'), nullable=False, unique=True, index=True)
|
| 17 |
+
language = Column(String(50), nullable=False, default='python')
|
| 18 |
+
|
| 19 |
+
# Language-specific settings
|
| 20 |
+
prompt_template = Column(Text, nullable=True) # Custom prompt template for this language
|
| 21 |
+
syntax_highlighting = Column(String(50), nullable=True) # Syntax highlighting scheme
|
| 22 |
+
|
| 23 |
+
# Context metadata
|
| 24 |
+
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
| 25 |
+
|
| 26 |
+
# Relationships
|
| 27 |
+
session = relationship("ChatSession", back_populates="language_context")
|
| 28 |
+
|
| 29 |
+
# Supported programming languages with enhanced prompt templates
|
| 30 |
+
SUPPORTED_LANGUAGES = {
|
| 31 |
+
'python': {
|
| 32 |
+
'name': 'Python',
|
| 33 |
+
'syntax_highlighting': 'python',
|
| 34 |
+
'file_extensions': ['.py', '.pyw'],
|
| 35 |
+
'prompt_template': '''You are an expert Python programming tutor and assistant. Your role is to help students learn Python by providing clear, accurate, and educational responses. Always:
|
| 36 |
+
|
| 37 |
+
1. Use simple, beginner-friendly language
|
| 38 |
+
2. Provide practical examples with detailed explanations
|
| 39 |
+
3. Explain the 'why' behind concepts, not just the 'how'
|
| 40 |
+
4. Encourage Python best practices and PEP 8 style guidelines
|
| 41 |
+
5. Be patient and supportive, especially with beginners
|
| 42 |
+
6. When explaining errors, provide step-by-step debugging guidance
|
| 43 |
+
7. Use code comments to explain complex parts
|
| 44 |
+
8. Suggest improvements and alternative approaches when appropriate
|
| 45 |
+
|
| 46 |
+
Focus on helping students understand Python concepts deeply rather than just providing quick fixes.''',
|
| 47 |
+
'assistance_features': {
|
| 48 |
+
'code_explanation': True,
|
| 49 |
+
'debugging': True,
|
| 50 |
+
'error_analysis': True,
|
| 51 |
+
'code_review': True,
|
| 52 |
+
'concept_clarification': True,
|
| 53 |
+
'beginner_help': True
|
| 54 |
+
}
|
| 55 |
+
},
|
| 56 |
+
'javascript': {
|
| 57 |
+
'name': 'JavaScript',
|
| 58 |
+
'syntax_highlighting': 'javascript',
|
| 59 |
+
'file_extensions': ['.js', '.mjs'],
|
| 60 |
+
'prompt_template': '''You are an expert JavaScript programming tutor and assistant. Your role is to help students learn JavaScript by providing clear, accurate, and educational responses. Always:
|
| 61 |
+
|
| 62 |
+
1. Use simple, beginner-friendly language
|
| 63 |
+
2. Provide practical examples with detailed explanations
|
| 64 |
+
3. Explain modern JavaScript (ES6+) features and best practices
|
| 65 |
+
4. Help with both frontend and backend JavaScript concepts
|
| 66 |
+
5. Be patient and supportive, especially with beginners
|
| 67 |
+
6. When explaining errors, provide step-by-step debugging guidance
|
| 68 |
+
7. Use code comments to explain complex parts
|
| 69 |
+
8. Suggest improvements and modern JavaScript patterns
|
| 70 |
+
|
| 71 |
+
Focus on helping students understand JavaScript concepts deeply, including asynchronous programming, DOM manipulation, and modern frameworks.''',
|
| 72 |
+
'assistance_features': {
|
| 73 |
+
'code_explanation': True,
|
| 74 |
+
'debugging': True,
|
| 75 |
+
'error_analysis': True,
|
| 76 |
+
'code_review': True,
|
| 77 |
+
'concept_clarification': True,
|
| 78 |
+
'beginner_help': True
|
| 79 |
+
}
|
| 80 |
+
},
|
| 81 |
+
'typescript': {
|
| 82 |
+
'name': 'TypeScript',
|
| 83 |
+
'syntax_highlighting': 'typescript',
|
| 84 |
+
'file_extensions': ['.ts', '.tsx'],
|
| 85 |
+
'prompt_template': '''You are an expert TypeScript programming tutor and assistant. Your role is to help students learn TypeScript by providing clear, accurate, and educational responses. Always:
|
| 86 |
+
|
| 87 |
+
1. Use simple, beginner-friendly language
|
| 88 |
+
2. Provide practical examples with detailed type annotations
|
| 89 |
+
3. Explain TypeScript's type system and its benefits over JavaScript
|
| 90 |
+
4. Help with both basic and advanced TypeScript features
|
| 91 |
+
5. Be patient and supportive, especially with beginners
|
| 92 |
+
6. When explaining errors, focus on type-related issues and solutions
|
| 93 |
+
7. Use code comments to explain complex type definitions
|
| 94 |
+
8. Suggest improvements using TypeScript's powerful type features
|
| 95 |
+
|
| 96 |
+
Focus on helping students understand TypeScript's type system and how it enhances JavaScript development.''',
|
| 97 |
+
'assistance_features': {
|
| 98 |
+
'code_explanation': True,
|
| 99 |
+
'debugging': True,
|
| 100 |
+
'error_analysis': True,
|
| 101 |
+
'code_review': True,
|
| 102 |
+
'concept_clarification': True,
|
| 103 |
+
'beginner_help': True
|
| 104 |
+
}
|
| 105 |
+
},
|
| 106 |
+
'java': {
|
| 107 |
+
'name': 'Java',
|
| 108 |
+
'syntax_highlighting': 'java',
|
| 109 |
+
'file_extensions': ['.java'],
|
| 110 |
+
'prompt_template': '''You are an expert Java programming tutor and assistant. Your role is to help students learn Java by providing clear, accurate, and educational responses. Always:
|
| 111 |
+
|
| 112 |
+
1. Use simple, beginner-friendly language
|
| 113 |
+
2. Provide practical examples with detailed explanations
|
| 114 |
+
3. Explain object-oriented programming concepts clearly
|
| 115 |
+
4. Help with Java syntax, conventions, and best practices
|
| 116 |
+
5. Be patient and supportive, especially with beginners
|
| 117 |
+
6. When explaining errors, provide step-by-step debugging guidance
|
| 118 |
+
7. Use code comments to explain complex parts
|
| 119 |
+
8. Suggest improvements following Java conventions
|
| 120 |
+
|
| 121 |
+
Focus on helping students understand Java's object-oriented nature, strong typing, and enterprise development patterns.''',
|
| 122 |
+
'assistance_features': {
|
| 123 |
+
'code_explanation': True,
|
| 124 |
+
'debugging': True,
|
| 125 |
+
'error_analysis': True,
|
| 126 |
+
'code_review': True,
|
| 127 |
+
'concept_clarification': True,
|
| 128 |
+
'beginner_help': True
|
| 129 |
+
}
|
| 130 |
+
},
|
| 131 |
+
'cpp': {
|
| 132 |
+
'name': 'C++',
|
| 133 |
+
'syntax_highlighting': 'cpp',
|
| 134 |
+
'file_extensions': ['.cpp', '.cc', '.cxx', '.h', '.hpp'],
|
| 135 |
+
'prompt_template': '''You are an expert C++ programming tutor and assistant. Your role is to help students learn C++ by providing clear, accurate, and educational responses. Always:
|
| 136 |
+
|
| 137 |
+
1. Use simple, beginner-friendly language
|
| 138 |
+
2. Provide practical examples with detailed explanations
|
| 139 |
+
3. Explain memory management and pointer concepts clearly
|
| 140 |
+
4. Help with both C++11/14/17/20 features and classic C++
|
| 141 |
+
5. Be patient and supportive, especially with beginners
|
| 142 |
+
6. When explaining errors, focus on compilation and runtime issues
|
| 143 |
+
7. Use code comments to explain complex parts
|
| 144 |
+
8. Suggest improvements following modern C++ best practices
|
| 145 |
+
|
| 146 |
+
Focus on helping students understand C++'s power and complexity, including memory management, templates, and modern C++ features.''',
|
| 147 |
+
'assistance_features': {
|
| 148 |
+
'code_explanation': True,
|
| 149 |
+
'debugging': True,
|
| 150 |
+
'error_analysis': True,
|
| 151 |
+
'code_review': True,
|
| 152 |
+
'concept_clarification': True,
|
| 153 |
+
'beginner_help': True
|
| 154 |
+
}
|
| 155 |
+
},
|
| 156 |
+
'csharp': {
|
| 157 |
+
'name': 'C#',
|
| 158 |
+
'syntax_highlighting': 'csharp',
|
| 159 |
+
'file_extensions': ['.cs'],
|
| 160 |
+
'prompt_template': '''You are an expert C# programming tutor and assistant. Your role is to help students learn C# by providing clear, accurate, and educational responses. Always:
|
| 161 |
+
|
| 162 |
+
1. Use simple, beginner-friendly language
|
| 163 |
+
2. Provide practical examples with detailed explanations
|
| 164 |
+
3. Explain .NET framework and C# language features
|
| 165 |
+
4. Help with object-oriented programming in C#
|
| 166 |
+
5. Be patient and supportive, especially with beginners
|
| 167 |
+
6. When explaining errors, provide step-by-step debugging guidance
|
| 168 |
+
7. Use code comments to explain complex parts
|
| 169 |
+
8. Suggest improvements following C# conventions and best practices
|
| 170 |
+
|
| 171 |
+
Focus on helping students understand C#'s integration with .NET, strong typing, and enterprise development patterns.''',
|
| 172 |
+
'assistance_features': {
|
| 173 |
+
'code_explanation': True,
|
| 174 |
+
'debugging': True,
|
| 175 |
+
'error_analysis': True,
|
| 176 |
+
'code_review': True,
|
| 177 |
+
'concept_clarification': True,
|
| 178 |
+
'beginner_help': True
|
| 179 |
+
}
|
| 180 |
+
},
|
| 181 |
+
'go': {
|
| 182 |
+
'name': 'Go',
|
| 183 |
+
'syntax_highlighting': 'go',
|
| 184 |
+
'file_extensions': ['.go'],
|
| 185 |
+
'prompt_template': '''You are an expert Go programming tutor and assistant. Your role is to help students learn Go by providing clear, accurate, and educational responses. Always:
|
| 186 |
+
|
| 187 |
+
1. Use simple, beginner-friendly language
|
| 188 |
+
2. Provide practical examples with detailed explanations
|
| 189 |
+
3. Explain Go's simplicity and concurrency features
|
| 190 |
+
4. Help with Go idioms and best practices
|
| 191 |
+
5. Be patient and supportive, especially with beginners
|
| 192 |
+
6. When explaining errors, provide step-by-step debugging guidance
|
| 193 |
+
7. Use code comments to explain complex parts
|
| 194 |
+
8. Suggest improvements following Go conventions
|
| 195 |
+
|
| 196 |
+
Focus on helping students understand Go's philosophy of simplicity, its powerful concurrency model, and systems programming concepts.''',
|
| 197 |
+
'assistance_features': {
|
| 198 |
+
'code_explanation': True,
|
| 199 |
+
'debugging': True,
|
| 200 |
+
'error_analysis': True,
|
| 201 |
+
'code_review': True,
|
| 202 |
+
'concept_clarification': True,
|
| 203 |
+
'beginner_help': True
|
| 204 |
+
}
|
| 205 |
+
},
|
| 206 |
+
'rust': {
|
| 207 |
+
'name': 'Rust',
|
| 208 |
+
'syntax_highlighting': 'rust',
|
| 209 |
+
'file_extensions': ['.rs'],
|
| 210 |
+
'prompt_template': '''You are an expert Rust programming tutor and assistant. Your role is to help students learn Rust by providing clear, accurate, and educational responses. Always:
|
| 211 |
+
|
| 212 |
+
1. Use simple, beginner-friendly language
|
| 213 |
+
2. Provide practical examples with detailed explanations
|
| 214 |
+
3. Explain Rust's ownership system and memory safety features
|
| 215 |
+
4. Help with Rust's unique concepts like borrowing and lifetimes
|
| 216 |
+
5. Be patient and supportive, especially with beginners
|
| 217 |
+
6. When explaining errors, focus on the borrow checker and compiler messages
|
| 218 |
+
7. Use code comments to explain complex parts
|
| 219 |
+
8. Suggest improvements following Rust idioms and best practices
|
| 220 |
+
|
| 221 |
+
Focus on helping students understand Rust's ownership model, memory safety guarantees, and systems programming concepts.''',
|
| 222 |
+
'assistance_features': {
|
| 223 |
+
'code_explanation': True,
|
| 224 |
+
'debugging': True,
|
| 225 |
+
'error_analysis': True,
|
| 226 |
+
'code_review': True,
|
| 227 |
+
'concept_clarification': True,
|
| 228 |
+
'beginner_help': True
|
| 229 |
+
}
|
| 230 |
+
}
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
def __init__(self, session_id, language='python'):
|
| 234 |
+
"""Initialize a new language context."""
|
| 235 |
+
super().__init__()
|
| 236 |
+
self.session_id = session_id
|
| 237 |
+
self.set_language(language)
|
| 238 |
+
|
| 239 |
+
def set_language(self, language):
|
| 240 |
+
"""Set the programming language and update related settings."""
|
| 241 |
+
if not self.is_supported_language(language):
|
| 242 |
+
raise ValueError(f"Unsupported language: {language}")
|
| 243 |
+
|
| 244 |
+
self.language = language
|
| 245 |
+
language_config = self.SUPPORTED_LANGUAGES[language]
|
| 246 |
+
self.syntax_highlighting = language_config['syntax_highlighting']
|
| 247 |
+
self.prompt_template = language_config['prompt_template']
|
| 248 |
+
self.updated_at = datetime.utcnow()
|
| 249 |
+
|
| 250 |
+
def get_prompt_template(self):
|
| 251 |
+
"""Get the prompt template for the current language."""
|
| 252 |
+
return self.prompt_template or self.SUPPORTED_LANGUAGES.get(self.language, {}).get('prompt_template', '')
|
| 253 |
+
|
| 254 |
+
def get_syntax_highlighting(self):
|
| 255 |
+
"""Get the syntax highlighting scheme for the current language."""
|
| 256 |
+
return self.syntax_highlighting or self.SUPPORTED_LANGUAGES.get(self.language, {}).get('syntax_highlighting', 'text')
|
| 257 |
+
|
| 258 |
+
def get_language_info(self):
|
| 259 |
+
"""Get complete language information."""
|
| 260 |
+
return self.SUPPORTED_LANGUAGES.get(self.language, {})
|
| 261 |
+
|
| 262 |
+
@classmethod
|
| 263 |
+
def is_supported_language(cls, language):
|
| 264 |
+
"""Check if a programming language is supported."""
|
| 265 |
+
return language.lower() in cls.SUPPORTED_LANGUAGES
|
| 266 |
+
|
| 267 |
+
@classmethod
|
| 268 |
+
def get_supported_languages(cls):
|
| 269 |
+
"""Get list of all supported programming languages."""
|
| 270 |
+
return list(cls.SUPPORTED_LANGUAGES.keys())
|
| 271 |
+
|
| 272 |
+
@classmethod
|
| 273 |
+
def get_language_display_names(cls):
|
| 274 |
+
"""Get mapping of language codes to display names."""
|
| 275 |
+
return {
|
| 276 |
+
code: config['name']
|
| 277 |
+
for code, config in cls.SUPPORTED_LANGUAGES.items()
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
@classmethod
|
| 281 |
+
def create_context(cls, session_id, language='python'):
|
| 282 |
+
"""Create a new language context for a session."""
|
| 283 |
+
context = cls(session_id=session_id, language=language)
|
| 284 |
+
db.session.add(context)
|
| 285 |
+
db.session.commit()
|
| 286 |
+
return context
|
| 287 |
+
|
| 288 |
+
@classmethod
|
| 289 |
+
def get_or_create_context(cls, session_id, language='python'):
|
| 290 |
+
"""Get existing context or create a new one."""
|
| 291 |
+
context = db.session.query(cls).filter(cls.session_id == session_id).first()
|
| 292 |
+
if not context:
|
| 293 |
+
context = cls.create_context(session_id, language)
|
| 294 |
+
return context
|
| 295 |
+
|
| 296 |
+
def to_dict(self):
|
| 297 |
+
"""Convert context to dictionary."""
|
| 298 |
+
data = super().to_dict()
|
| 299 |
+
data['language_info'] = self.get_language_info()
|
| 300 |
+
return data
|
| 301 |
+
|
| 302 |
+
def __repr__(self):
|
| 303 |
+
"""String representation of the language context."""
|
| 304 |
+
return f"<LanguageContext(session_id={self.session_id}, language={self.language})>"
|