diff --git a/.gitignore b/.gitignore index 09b952a..282b4d1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ # Remy's trash/ experiments/ -.DS_Store +*.DS_Store *.log.[1-9] *.mov *.db diff --git a/README.md b/README.md index 7e67ea7..d7ea06c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Summoner Core SDK

- +

> The **Summoner Core SDK** is the foundation of the Summoner protocol, providing a minimal, composable runtime for building and coordinating autonomous agents. diff --git a/img/2sellers_2buyers.gif b/assets/gifs/2sellers_2buyers.gif similarity index 100% rename from img/2sellers_2buyers.gif rename to assets/gifs/2sellers_2buyers.gif diff --git a/img/merchants_demo.gif b/assets/gifs/merchants_demo.gif similarity index 100% rename from img/merchants_demo.gif rename to assets/gifs/merchants_demo.gif diff --git a/img/travel_demo.gif b/assets/gifs/travel_demo.gif similarity index 100% rename from img/travel_demo.gif rename to assets/gifs/travel_demo.gif diff --git a/img/druid_agent.png b/assets/img/druid_agent.png similarity index 100% rename from img/druid_agent.png rename to assets/img/druid_agent.png diff --git a/img/92a3447d-6925-431e-a2d0-a1ee671cd9bd.png b/assets/img/knight_n_animals.png similarity index 100% rename from img/92a3447d-6925-431e-a2d0-a1ee671cd9bd.png rename to assets/img/knight_n_animals.png diff --git a/img/merchants.png b/assets/img/merchants.png similarity index 100% rename from img/merchants.png rename to assets/img/merchants.png diff --git a/assets/img/summoner_user_rounded.png b/assets/img/summoner_user_rounded.png new file mode 100644 index 0000000..d618ab6 Binary files /dev/null and b/assets/img/summoner_user_rounded.png differ diff --git a/examples/1_chat_agents/myserver.py b/examples/1_chat_agents/myserver.py deleted file mode 100644 index 4b645f1..0000000 --- a/examples/1_chat_agents/myserver.py +++ /dev/null @@ -1,10 +0,0 @@ -from summoner.server import SummonerServer -import argparse - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Run a Summoner server with a specified config.") - parser.add_argument('--config', dest='config_path', required=True, help='The relative path to the config file (JSON) for the server (e.g., --config myproject/server_config.json)') - args = parser.parse_args() - - myserver = SummonerServer(name="MyServer") - myserver.run(config_path=args.config_path) diff --git a/examples/1_chat_agents/readme.md b/examples/1_chat_agents/readme.md index 7c2ee18..f7df4c9 100644 --- a/examples/1_chat_agents/readme.md +++ b/examples/1_chat_agents/readme.md @@ -3,5 +3,5 @@ Chat agent with traveling capabilities

- +

\ No newline at end of file diff --git a/examples/1_chat_agents/server_config.json b/examples/1_chat_agents/server_config.json deleted file mode 100644 index 547e659..0000000 --- a/examples/1_chat_agents/server_config.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "host": "127.0.0.1", - "port": 8888, - - "version": "rust", - - "logger": { - "log_level": "INFO", - - "enable_console_log": true, - "console_log_format": "\u001b[92m%(asctime)s\u001b[0m - \u001b[94m%(name)s\u001b[0m - %(levelname)s - %(message)s", - - "enable_file_log": true, - "enable_json_log": true, - "log_file_path": "logs/", - "log_format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s", - - "max_file_size": 1000000, - "backup_count": 3, - "date_format": "%Y-%m-%d %H:%M:%S.%3f", - "log_keys": null - }, - - "hyper_parameters": { - - "connection_buffer_size": 256, - "command_buffer_size": 64, - "control_channel_capacity": 8, - "queue_monitor_capacity": 100, - - "client_timeout_secs": 600, - "rate_limit_msgs_per_minute": 1000, - "timeout_check_interval_secs": 30, - "accept_error_backoff_ms": 100, - - "quarantine_cooldown_secs": 600, - "quarantine_cleanup_interval_secs": 60, - - "throttle_delay_ms": 200, - "flow_control_delay_ms": 1000, - - "worker_threads": 4, - - "backpressure_policy": { - "enable_throttle": true, - "throttle_threshold": 50, - "enable_flow_control": true, - "flow_control_threshold": 150, - "enable_disconnect": true, - "disconnect_threshold": 300 - } - } -} \ No newline at end of file diff --git a/examples/2_question_answer_agents/myserver.py b/examples/2_question_answer_agents/myserver.py deleted file mode 100644 index 562887c..0000000 --- a/examples/2_question_answer_agents/myserver.py +++ /dev/null @@ -1,10 +0,0 @@ -import argparse -from summoner.server import SummonerServer - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Run a Summoner server with a specified config.") - parser.add_argument('--config', dest='config_path', required=True, help='The relative path to the config file (JSON) for the server (e.g., --config myproject/server_config.json)') - args = parser.parse_args() - - myserver = SummonerServer(name="MyServer") - myserver.run(config_path=args.config_path) diff --git a/examples/2_question_answer_agents/server_config.json b/examples/2_question_answer_agents/server_config.json deleted file mode 100644 index d78b972..0000000 --- a/examples/2_question_answer_agents/server_config.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "host": "127.0.0.1", - "port": 8888, - - "version": "rust", - - "logger": { - "log_level": "INFO", - - "enable_console_log": true, - "console_log_format": "\u001b[92m%(asctime)s\u001b[0m - \u001b[94m%(name)s\u001b[0m - %(levelname)s - %(message)s", - - "enable_file_log": true, - "enable_json_log": true, - "log_file_path": "logs/", - "log_format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s", - - "max_file_size": 1000000, - "backup_count": 3, - "date_format": "%Y-%m-%d %H:%M:%S.%3f", - "log_keys": null - }, - - "hyper_parameters": { - - "connection_buffer_size": 256, - "command_buffer_size": 64, - "control_channel_capacity": 8, - "queue_monitor_capacity": 100, - - "client_timeout_secs": 600, - "rate_limit_msgs_per_minute": 1000, - "timeout_check_interval_secs": 30, - "accept_error_backoff_ms": 100, - - "quarantine_cooldown_secs": 600, - "quarantine_cleanup_interval_secs": 60, - - "throttle_delay_ms": 200, - "flow_control_delay_ms": 1000, - - "worker_threads": 4, - - "backpressure_policy": { - "enable_throttle": true, - "throttle_threshold": 50, - "enable_flow_control": true, - "flow_control_threshold": 150, - "enable_disconnect": true, - "disconnect_threshold": 300 - } - } -} \ No newline at end of file diff --git a/examples/3_buyer_seller_agents/db_files/db_sdk.py b/examples/3_buyer_seller_agents/db_files/db_sdk.py deleted file mode 100644 index 78fe3d8..0000000 --- a/examples/3_buyer_seller_agents/db_files/db_sdk.py +++ /dev/null @@ -1,224 +0,0 @@ -import aiosqlite -from pathlib import Path -from datetime import datetime -import random -import uuid - -# Module-level database path; set via configure_db_path() -_DB_PATH: Path = None - -def configure_db_path(path: Path): - """ - Configure the SQLite file path for this agent. - """ - global _DB_PATH - _DB_PATH = path - - -def get_db_path() -> Path: - """ - Return the configured DB path, or default under this module. - """ - if _DB_PATH: - base = Path(_DB_PATH) - base.parent.mkdir(parents=True, exist_ok=True) - return base - base = Path(__file__).resolve().parent - base.mkdir(parents=True, exist_ok=True) - return base / "negotiation_history.db" - -async def init_db(): - """ - Create 'state' and 'history' tables if they do not exist, - and ensure history(agent_id, txid) is unique. - """ - db_file = get_db_path() - async with aiosqlite.connect(str(db_file)) as db: - # state table - await db.execute(""" - CREATE TABLE IF NOT EXISTS state ( - agent_id TEXT PRIMARY KEY, - transaction_id TEXT, - current_offer REAL, - agreement TEXT, - negotiation_active INTEGER, - limit_acceptable_price REAL, - price_shift REAL - ) - """) - - # history table (without uniqueness on agent_id+txid) - await db.execute(""" - CREATE TABLE IF NOT EXISTS history ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT, - success INTEGER CHECK(success IN (0,1)), - txid TEXT, - timestamp TEXT - ) - """) - - # now enforce one row per (agent_id, txid) - await db.execute(""" - CREATE UNIQUE INDEX IF NOT EXISTS - idx_history_agent_tx - ON history(agent_id, txid) - """) - - await db.commit() - -async def create_or_reset_state(agent_id: str): - """ - Ensure a 'state' row exists for this agent; do not overwrite existing negotiations. - """ - db_file = get_db_path() - async with aiosqlite.connect(str(db_file)) as db: - # only insert default row if none exists - await db.execute( - """ - INSERT OR IGNORE INTO state ( - agent_id, transaction_id, current_offer, - agreement, negotiation_active, - limit_acceptable_price, price_shift - ) VALUES (?, NULL, 0, 'none', 0, 0, 0) - """, - (agent_id,) - ) - await db.commit() - -async def set_state_fields(agent_id: str, **fields): - """ - Update arbitrary fields for a agent_id in 'state'. - """ - if not fields: - return - columns = ", ".join(f"{col} = ?" for col in fields.keys()) - params = list(fields.values()) + [agent_id] - db_file = get_db_path() - async with aiosqlite.connect(str(db_file)) as db: - await db.execute(f"UPDATE state SET {columns} WHERE agent_id = ?", params) - await db.commit() - -async def get_state(agent_id: str) -> dict: - """ - Return the state row as a dict, or None if missing. - """ - db_file = get_db_path() - async with aiosqlite.connect(str(db_file)) as db: - cur = await db.execute( - "SELECT transaction_id, current_offer, agreement, negotiation_active," - " limit_acceptable_price, price_shift" - " FROM state WHERE agent_id = ?", - (agent_id,) - ) - row = await cur.fetchone() - if not row: - return None - return { - "transaction_id": row[0], - "current_offer": row[1], - "agreement": row[2], - "negotiation_active": bool(row[3]), - "limit_acceptable_price": row[4], - "price_shift": row[5], - } - -async def get_active_agents() -> list[str]: - """ - Return list of agent_ids with an active negotiation. - """ - db_file = get_db_path() - async with aiosqlite.connect(str(db_file)) as db: - cur = await db.execute( - "SELECT agent_id FROM state WHERE negotiation_active = 1" - ) - rows = await cur.fetchall() - return [r[0] for r in rows] - -async def add_history(agent_id: str, success: int, txid: str) -> bool: - """ - Insert one record into 'history' unless (agent_id,txid) already exists. - Returns False if this insertion was skipped (duplicate), False if a new row was inserted. - """ - timestamp = datetime.now().isoformat() - db_file = get_db_path() - - async with aiosqlite.connect(str(db_file)) as db: - cur = await db.execute( - """ - INSERT INTO history (agent_id, success, txid, timestamp) - VALUES (?, ?, ?, ?) - ON CONFLICT(agent_id, txid) DO NOTHING - """, - (agent_id, success, txid, timestamp) - ) - await db.commit() - - # rowcount == 1 → inserted; rowcount == 0 → skipped - added = (cur.rowcount != 0) - return added - -async def get_history(agent_id: str) -> list: - """ - Return list of (success, txid) for this agent. - """ - db_file = get_db_path() - async with aiosqlite.connect(str(db_file)) as db: - cur = await db.execute( - "SELECT success, txid FROM history WHERE agent_id = ? ORDER BY id", - (agent_id,) - ) - return await cur.fetchall() - -async def show_statistics(agent_id: str): - """ - Print success rate and last txid for agent. - """ - history = await get_history(agent_id) - total = len(history) - successes = sum(s for s,_ in history) - last_txid = history[-1][1] if total else None - rate = (successes/total*100) if total else 0 - return { - "agent_id": agent_id, - "rate": rate, - "successes": successes, - "total": total, - "last_txid": last_txid - } - -async def start_negotiation_seller(agent_id: str) -> str: - """ - Initialize a fresh negotiation for agent_id, randomize terms, return txid. - """ - min_p = random.randint(60, 90) - dec = random.randint(1, 5) - curr = random.randint(min_p, 100) - txid = str(uuid.uuid4()) - await set_state_fields( - agent_id, - limit_acceptable_price=min_p, - price_shift=dec, - current_offer=curr, - agreement="none", - negotiation_active=1, - transaction_id=txid, - ) - return txid - -async def start_negotiation_buyer(agent_id: str, txid: str) -> str: - """ - Initialize a fresh negotiation for agent_id, randomize terms, return txid. - """ - max_p = random.randint(65, 80) - inc = random.randint(1, 5) - curr = random.randint(1, max_p) - await set_state_fields( - agent_id, - limit_acceptable_price=max_p, - price_shift=inc, - current_offer=curr, - agreement="none", - negotiation_active=1, - transaction_id=txid - ) \ No newline at end of file diff --git a/examples/3_buyer_seller_agents/myserver.py b/examples/3_buyer_seller_agents/myserver.py deleted file mode 100644 index 562887c..0000000 --- a/examples/3_buyer_seller_agents/myserver.py +++ /dev/null @@ -1,10 +0,0 @@ -import argparse -from summoner.server import SummonerServer - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Run a Summoner server with a specified config.") - parser.add_argument('--config', dest='config_path', required=True, help='The relative path to the config file (JSON) for the server (e.g., --config myproject/server_config.json)') - args = parser.parse_args() - - myserver = SummonerServer(name="MyServer") - myserver.run(config_path=args.config_path) diff --git a/examples/3_buyer_seller_agents/readme.md b/examples/3_buyer_seller_agents/readme.md index f2add70..1e97870 100644 --- a/examples/3_buyer_seller_agents/readme.md +++ b/examples/3_buyer_seller_agents/readme.md @@ -1,201 +1,17 @@ # Agent-Based Price Negotiation Protocol -

- -

- -This README provides a detailed walkthrough of an **automated negotiation protocol** between two autonomous agents: **SellerAgent** and **BuyerAgent**. The protocol demonstrates **asynchronous communication**, **dynamic pricing strategies**, **randomized behaviors**, and **real-time tracking of negotiation outcomes**. - - -## Overview - -In this system, two agents engage in a **continuous price negotiation**: -- The **buyer** tries to purchase at the **lowest possible price**, adjusting upward if needed. -- The **seller** tries to sell at the **highest possible price**, adjusting downward if needed. -- A negotiation **succeeds** when both agents agree on a price. -- Otherwise, the negotiation **fails** and a new session begins. - -Each negotiation round uses **randomized initial prices** and **adjustments**, making the interactions **diverse** and **unpredictable** across time. - - -## Live Interaction Demo +You can observe how each agent proposes, counters, and dynamically adjusts their offers before reaching an agreement, or terminating the negotiation. -The following animation shows a **real-time exchange** between the BuyerAgent and SellerAgent during negotiation:

- +

-You can observe how each agent proposes, counters, and dynamically adjusts their offers before reaching an agreement, or terminating the negotiation. +The BuyerAgent and SellerAgent are designed to handle negotiations in noisy environments where many interactions occur concurrently. -The BuyerAgent and SellerAgent are designed to handle negotiations in noisy environments where many interactions occur concurrently. The following demo illustrates this by running two sellers and two buyers — resulting in eight simultaneous negotiations—on a single server instance. +The following demo illustrates this by running two sellers and two buyers — resulting in eight simultaneous negotiations—on a single server instance.

- +

- -## Key Features - -- **Dynamic and random price settings** for each negotiation round. -- **Asynchronous message exchange** using Python's `asyncio`. -- **Adaptive behavior** based on received offers. -- **Real-time success rate tracking** after every negotiation. -- **Continuous execution**: new negotiations start automatically. - - -## How the Negotiation Works - -The agents communicate back and forth over a TCP connection. Each side follows a simple **protocol of messages**. - -### BuyerAgent Behavior -- Starts with a **low initial offer** (random between 1 and their `MAX_ACCEPTABLE_PRICE`). -- **Increases** the offer gradually if necessary (by a random increment). -- Accepts an offer if the price is **affordable**. -- Refuses an offer if the seller's price remains **too high**. - -### SellerAgent Behavior -- Starts with a **high initial offer** (random between `MIN_ACCEPTABLE_PRICE` and 100). -- **Decreases** the offer gradually (by a random decrement). -- Accepts an offer if the price is **high enough**. -- Refuses if the buyer's offer is **too low**. - -### Message Types Exchanged - -| Message Type | Meaning | -|--------------|---------| -| `offer` | A proposed price (not yet a commitment). | -| `interested` | Willingness to accept if conditions improve slightly. | -| `accept` | Acceptance of the current offer. | -| `refuse` | Refusal of the current offer. | - -Both agents react based on the message type and the price offered. - - -## Detailed Negotiation Flow - -```mermaid -sequenceDiagram -participant BuyerAgent -participant SellerAgent - -BuyerAgent ->> SellerAgent: Sends offer (price) -SellerAgent -->> BuyerAgent: Responds (offer, interested, accept, refuse) -BuyerAgent -->> SellerAgent: Reacts (offer, interested, accept, refuse) -SellerAgent -->> BuyerAgent: Reacts again (if needed) -Note right of BuyerAgent: Negotiation ends successfully or fails. -``` - -### Step-by-Step - -1. **Buyer** starts by sending an initial offer. -2. **Seller** evaluates: - - Accepts if acceptable. - - Decreases price if necessary. - - Otherwise, refuses. -3. **Buyer** evaluates the seller's response: - - Increases offer if needed. - - Shows interest if close to agreement. - - Accepts or refuses depending on the situation. -4. **Success or failure** is recorded. -5. **New negotiation round** starts automatically. - - -## Code Structure - -Both agents are structured around asynchronous loops and event-driven reactions: - -### BuyerAgent - -```plaintext -BuyerAgent -│ -├── Randomly set MAX_ACCEPTABLE_PRICE and PRICE_INCREMENT -│ -├── Negotiation loop: -│ ├── Start new negotiation -│ ├── Send initial offer -│ └── React to seller responses -│ ├── Increase offer if needed -│ ├── Express interest -│ ├── Accept or refuse -│ -└── Track success/failure after each negotiation -``` - -### SellerAgent - -```plaintext -SellerAgent -│ -├── Randomly set MIN_ACCEPTABLE_PRICE and PRICE_DECREMENT -│ -├── Negotiation loop: -│ ├── Start new negotiation -│ ├── Send initial offer -│ └── React to buyer offers -│ ├── Decrease price if needed -│ ├── Express interest -│ ├── Accept or refuse -│ -└── Track success/failure after each negotiation -``` - -## Success Rate Tracking - -Each agent keeps a **history list**: -- `1` = successful negotiation (deal made). -- `0` = unsuccessful negotiation (deal failed). - -After each round, the agent prints: - -```plaintext -[AgentName] Negotiation success rate: XX.XX% (Y successes / Z total) -``` - -The success rate is calculated as: - -\[ -\text{Success Rate} = \left( \frac{\text{Number of Successful Negotiations}}{\text{Total Negotiations}} \right) \times 100 -\] - - -## How to Run - -Open three terminals. - -In the first terminal (SellerAgent): - -```bash -python examples/3_buyer_seller_agents/seller_agent.py -``` - -In the second terminal (BuyerAgent): - -```bash -python examples/3_buyer_seller_agents/buyer_agent.py -``` - -In the third terminal (MyServer): - -```bash -python examples/3_buyer_seller_agents/myserver.py --config server_config.json -``` - -The agents will connect locally (`127.0.0.1:8888`) and automatically start negotiating continuously. - ---- - -## Notes - -- Buyer sends a message approximately **every 2 seconds**. -- Seller sends a message approximately **every 3 seconds**. -- This slight mismatch leads to natural asynchronous behavior where agents might act before receiving a response. -- Each negotiation is independent thanks to randomized price settings at the start of each round. - - -## Conclusion - -This example shows how to build simple, **adaptive**, **asynchronous**, and **self-evolving** agents that negotiate automatically. You can observe different outcomes depending on the random parameters, leading to either quick agreements or prolonged, unsuccessful negotiations. - -The pattern here is fundamental to designing larger multi-agent negotiation or trading systems. diff --git a/examples/3_buyer_seller_agents/server_config.json b/examples/3_buyer_seller_agents/server_config.json deleted file mode 100644 index 3d2451e..0000000 --- a/examples/3_buyer_seller_agents/server_config.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "host": "127.0.0.1", - "port": 8888, - - "version": "rust", - - "logger": { - "log_level": "INFO", - - "enable_console_log": true, - "console_log_format": "\u001b[92m%(asctime)s\u001b[0m - \u001b[94m%(name)s\u001b[0m - %(levelname)s - %(message)s", - - "enable_file_log": true, - "enable_json_log": true, - "log_file_path": "logs/", - "log_format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s", - - "max_file_size": 1000000, - "backup_count": 3, - "date_format": "%Y-%m-%d %H:%M:%S.%3f", - "log_keys": null - }, - - "hyper_parameters": { - - "connection_buffer_size": 256, - "command_buffer_size": 64, - "control_channel_capacity": 8, - "queue_monitor_capacity": 100, - - "client_timeout_secs": 600, - "rate_limit_msgs_per_minute": 1000, - "timeout_check_interval_secs": 30, - "accept_error_backoff_ms": 100, - - "quarantine_cooldown_secs": 600, - "quarantine_cleanup_interval_secs": 60, - - "throttle_delay_ms": 200, - "flow_control_delay_ms": 1000, - - "worker_threads": 4, - - "backpressure_policy": { - "enable_throttle": true, - "throttle_threshold": 50, - "enable_flow_control": true, - "flow_control_threshold": 150, - "enable_disconnect": true, - "disconnect_threshold": 300 - } - } -} \ No newline at end of file diff --git a/examples/4_spell_dna_agents/myserver.py b/examples/4_spell_dna_agents/myserver.py deleted file mode 100644 index 4b645f1..0000000 --- a/examples/4_spell_dna_agents/myserver.py +++ /dev/null @@ -1,10 +0,0 @@ -from summoner.server import SummonerServer -import argparse - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Run a Summoner server with a specified config.") - parser.add_argument('--config', dest='config_path', required=True, help='The relative path to the config file (JSON) for the server (e.g., --config myproject/server_config.json)') - args = parser.parse_args() - - myserver = SummonerServer(name="MyServer") - myserver.run(config_path=args.config_path) diff --git a/examples/4_spell_dna_agents/server_config.json b/examples/4_spell_dna_agents/server_config.json deleted file mode 100644 index 547e659..0000000 --- a/examples/4_spell_dna_agents/server_config.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "host": "127.0.0.1", - "port": 8888, - - "version": "rust", - - "logger": { - "log_level": "INFO", - - "enable_console_log": true, - "console_log_format": "\u001b[92m%(asctime)s\u001b[0m - \u001b[94m%(name)s\u001b[0m - %(levelname)s - %(message)s", - - "enable_file_log": true, - "enable_json_log": true, - "log_file_path": "logs/", - "log_format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s", - - "max_file_size": 1000000, - "backup_count": 3, - "date_format": "%Y-%m-%d %H:%M:%S.%3f", - "log_keys": null - }, - - "hyper_parameters": { - - "connection_buffer_size": 256, - "command_buffer_size": 64, - "control_channel_capacity": 8, - "queue_monitor_capacity": 100, - - "client_timeout_secs": 600, - "rate_limit_msgs_per_minute": 1000, - "timeout_check_interval_secs": 30, - "accept_error_backoff_ms": 100, - - "quarantine_cooldown_secs": 600, - "quarantine_cleanup_interval_secs": 60, - - "throttle_delay_ms": 200, - "flow_control_delay_ms": 1000, - - "worker_threads": 4, - - "backpressure_policy": { - "enable_throttle": true, - "throttle_threshold": 50, - "enable_flow_control": true, - "flow_control_threshold": 150, - "enable_disconnect": true, - "disconnect_threshold": 300 - } - } -} \ No newline at end of file diff --git a/img/buyer_seller.png b/img/buyer_seller.png deleted file mode 100644 index 83808a5..0000000 Binary files a/img/buyer_seller.png and /dev/null differ diff --git a/img/make_demo_gif.sh b/img/make_demo_gif.sh deleted file mode 100755 index 0d03a6f..0000000 --- a/img/make_demo_gif.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -if [ $# -ne 1 ]; then - echo "$ cd zzz/" - echo "$ ls" - echo "$0" - echo "assets/" - echo "" - echo "Usage: $0 " - echo "Example: $0 merchants_demo" - exit 1 -fi - -NAME="$1" -INPUT="assets/${NAME}.mov" -PALETTE="assets/${NAME}_palette.png" -OUTPUT="assets/${NAME}.gif" - -# sanity check -if [ ! -f "$INPUT" ]; then - echo "Error: Input file not found: $INPUT" - exit 1 -fi - -echo "Generating palette for $INPUT → $PALETTE" -ffmpeg -y -i "$INPUT" \ - -vf "fps=25,scale=800:-1:flags=lanczos,palettegen" \ - "$PALETTE" - -echo "Encoding GIF for $INPUT → $OUTPUT" -ffmpeg -i "$INPUT" -i "$PALETTE" \ - -filter_complex "fps=25,scale=800:-1:flags=lanczos[x];[x][1:v]paletteuse" \ - "$OUTPUT" - -echo "Done! → $OUTPUT" diff --git a/img/make_gif.sh b/img/make_gif.sh deleted file mode 100644 index 957973a..0000000 --- a/img/make_gif.sh +++ /dev/null @@ -1,4 +0,0 @@ -ffmpeg -y -i img/merchants_demo.mov -vf "fps=25,scale=800:-1:flags=lanczos,palettegen" img/palette.png - -ffmpeg -i img/merchants_demo.mov -i img/palette.png -filter_complex "fps=25,scale=800:-1:flags=lanczos[x];[x][1:v]paletteuse" img/merchants_demo.gif - diff --git a/img/palette.png b/img/palette.png deleted file mode 100644 index 1eac328..0000000 Binary files a/img/palette.png and /dev/null differ diff --git a/img/protocol_v1.png b/img/protocol_v1.png deleted file mode 100644 index 2c8fd8b..0000000 Binary files a/img/protocol_v1.png and /dev/null differ diff --git a/img/protocol_v2.png b/img/protocol_v2.png deleted file mode 100644 index 6476f70..0000000 Binary files a/img/protocol_v2.png and /dev/null differ diff --git a/img/question_answer.png b/img/question_answer.png deleted file mode 100644 index aee36a7..0000000 Binary files a/img/question_answer.png and /dev/null differ diff --git a/img/summoner_user.png b/img/summoner_user.png deleted file mode 100644 index 36138b2..0000000 Binary files a/img/summoner_user.png and /dev/null differ