Skip to content

Commit

Permalink
add tutorials
Browse files Browse the repository at this point in the history
  • Loading branch information
dewmal committed Jan 25, 2025
1 parent 1907374 commit f3f3f5e
Show file tree
Hide file tree
Showing 7 changed files with 911 additions and 723 deletions.
301 changes: 157 additions & 144 deletions bindings/ceylon/examples/auction/auction_general.md
Original file line number Diff line number Diff line change
@@ -1,183 +1,196 @@
# Building a Multi-Agent Meeting Scheduler System
# Building a Distributed Auction System with Ceylon

## Core Components
This tutorial demonstrates how to build a distributed auction system using the Ceylon multi-agent framework. The system consists of an auctioneer agent managing the auction process and multiple bidder agents competing for items.

### Data Models
## System Overview

```python
@dataclass
class TimeSlot:
date: str
start_time: int
end_time: int
The auction system implements:
- Single-item auctions with multiple bidders
- Automatic bid placement based on budget constraints
- Real-time auction status updates
- Distributed communication between auctioneer and bidders

@property
def duration(self):
return self.end_time - self.start_time
## Core Components

### Data Models

```python
@dataclass
class Meeting:
class Item:
name: str
date: str
duration: int
minimum_participants: int
starting_price: float

@dataclass
class Bid:
bidder: str
amount: float

@dataclass
class AvailabilityRequest:
time_slot: TimeSlot
class AuctionStart:
item: Item

@dataclass
class AuctionResult:
winner: str
winning_bid: float

@dataclass
class AvailabilityResponse:
owner: str
time_slot: TimeSlot
accepted: bool
class AuctionEnd:
pass
```

### Participant Agent
### Auctioneer Agent

The auctioneer manages the auction process:

```python
class Participant(Worker):
def __init__(self, name: str, available_times: list[TimeSlot]):
super().__init__(name=name, role="participant")
self.available_times = available_times

@staticmethod
def is_overlap(slot1: TimeSlot, slot2: TimeSlot, duration: int) -> bool:
latest_start = max(slot1.start_time, slot2.start_time)
earliest_end = min(slot1.end_time, slot2.end_time)
return earliest_end - latest_start >= duration

@on(AvailabilityRequest)
async def handle_availability_request(self, data: AvailabilityRequest,
time: int, agent: AgentDetail):
is_available = any(self.is_overlap(slot, data.time_slot,
data.time_slot.duration)
for slot in self.available_times)
await self.broadcast_message(AvailabilityResponse(
owner=self.details().name,
time_slot=data.time_slot,
accepted=is_available
))
class Auctioneer(BaseAgent):
def __init__(self, item: Item, expected_bidders: int, name="auctioneer", port=8888):
super().__init__(
name=name,
mode=PeerMode.ADMIN,
role="auctioneer",
port=port
)
self.item = item
self.expected_bidders = expected_bidders
self.bids: List[Bid] = []
self.auction_ended = False
```

### Coordinator Agent
Key methods:
- `handle_connection`: Monitors bidder connections and starts auction when all bidders join
- `handle_bid`: Processes incoming bids
- `end_auction`: Determines winner and broadcasts results

### Bidder Agent

Each bidder participates in the auction:

```python
class Coordinator(Admin):
def __init__(self, name: str, port: int):
super().__init__(name=name, port=port)
self.meeting_request = None
self.agreed_slots = {}
self.next_time_slot = None

@on_run()
async def handle_run(self, inputs: Meeting):
self.meeting_request = inputs

@on_connect("*")
async def handle_connection(self, topic: str, agent: AgentDetail):
start_time = 8
self.next_time_slot = TimeSlot(
self.meeting_request.date,
start_time,
start_time + self.meeting_request.duration
)
await self.broadcast_message(
AvailabilityRequest(time_slot=self.next_time_slot)
class Bidder(BaseAgent):
def __init__(self, name: str, budget: float,
workspace_id=DEFAULT_WORKSPACE_ID,
admin_peer="",
admin_port=8888):
super().__init__(
name=name,
mode=PeerMode.CLIENT,
role="bidder"
)

@on(AvailabilityResponse)
async def handle_availability_response(self, data: AvailabilityResponse,
time: int, agent: AgentDetail):
if not data.accepted:
current_slot = data.time_slot
next_slot = TimeSlot(
self.meeting_request.date,
current_slot.start_time + 1,
current_slot.start_time + 1 + self.meeting_request.duration
)
if next_slot.end_time > self.next_time_slot.end_time:
self.next_time_slot = next_slot
await self.broadcast_message(
AvailabilityRequest(time_slot=self.next_time_slot)
)
return

time_slot_key = str(data.time_slot)
slots = self.agreed_slots.get(time_slot_key, [])
if data.owner not in slots:
slots.append(data.owner)
self.agreed_slots[time_slot_key] = slots
if len(slots) >= self.meeting_request.minimum_participants:
await self.stop()
self.budget = budget
self.has_bid = False
```

## System Usage
Key methods:
- `handle_auction_start`: Places bid when auction begins
- `handle_auction_result`: Processes auction results
- `handle_auction_end`: Acknowledges auction completion

## Bidding Strategy

Bidders use a simple random strategy:
```python
async def main():
participants = [
Participant("Alice", [
TimeSlot("2024-07-21", 9, 12),
TimeSlot("2024-07-21", 14, 18)
]),
Participant("Bob", [
TimeSlot("2024-07-21", 10, 13),
TimeSlot("2024-07-21", 15, 17)
]),
]

admin = Coordinator(name="admin", port=8888)
meeting = Meeting(
name="Meeting 1",
duration=1,
date="2024-07-21",
minimum_participants=3
)

await admin.start_agent(
inputs=pickle.dumps(meeting),
workers=participants
)


if __name__ == '__main__':
asyncio.run(main())
random_multiplier = random.randint(100, 1000) / 100
bid_amount = min(self.budget, auction_start.item.starting_price * random_multiplier)
```

## Key Features

1. Time slot management with overlap detection
2. Asynchronous availability requests/responses
3. Automatic meeting slot negotiation
4. Minimum participant requirement tracking
5. Decentralized participant coordination
## Running the System

## Customization Options
1. Create auction item and auctioneer:
```python
item = Item("Rare Painting", 1000.0)
auctioneer = Auctioneer(item, expected_bidders=3, port=8455)
admin_details = auctioneer.details()
```

1. Add priority-based scheduling:
2. Create bidders:
```python
bidders = [
Bidder("Alice", 1500.0, admin_peer=admin_details.id),
Bidder("Bob", 1200.0, admin_peer=admin_details.id),
Bidder("Charlie", 2000.0, admin_peer=admin_details.id)
]
```

3. Start the system:
```python
@dataclass
class Meeting:
priority: int # Add priority field
await auctioneer.start_agent(b"", bidders)
```

2. Implement preferred time slots:
## Sample Output

```python
@dataclass
class AvailabilityResponse:
preference_score: int # Add preference rating
```
ceylon version: 0.22.1
visit https://ceylon.ai for more information
2025-01-26 00:04:41.323 | INFO | __main__:<module>:161 - Initializing auction system...
2025-01-26 00:04:41.327 | INFO | __main__:main:155 - Starting auction system...
2025-01-26 00:04:41.327 | INFO | ceylon.base.uni_agent:start_agent:76 - Starting auctioneer agent in ADMIN mode
2025-01-26 00:04:41.389 | INFO | __main__:handle_run:91 - Auctioneer started - auctioneer
2025-01-26 00:04:41.389 | INFO | __main__:handle_run:138 - Bidder started - Alice
2025-01-26 00:04:41.389 | INFO | __main__:handle_run:138 - Bidder started - Bob
2025-01-26 00:04:41.389 | INFO | __main__:handle_run:138 - Bidder started - Charlie
2025-01-26 00:04:41.389 | INFO | __main__:handle_run:138 - Bidder started - Jon
2025-01-26 00:04:41.548 | INFO | __main__:handle_connection:50 - Bidder Alice connected with auctioneer. 0/3 bidders connected.
2025-01-26 00:04:41.548 | INFO | __main__:handle_connection:57 - Waiting for more bidders to connect...
2025-01-26 00:04:41.549 | INFO | __main__:handle_connection:50 - Bidder Bob connected with auctioneer. 1/3 bidders connected.
2025-01-26 00:04:41.549 | INFO | __main__:handle_connection:57 - Waiting for more bidders to connect...
2025-01-26 00:04:41.589 | INFO | __main__:handle_connection:50 - Bidder Jon connected with auctioneer. 2/3 bidders connected.
2025-01-26 00:04:41.589 | INFO | __main__:handle_connection:57 - Waiting for more bidders to connect...
2025-01-26 00:04:41.591 | INFO | __main__:handle_connection:50 - Bidder Charlie connected with auctioneer. 3/3 bidders connected.
2025-01-26 00:04:41.591 | INFO | __main__:handle_connection:54 - All bidders connected. Starting the auction.
2025-01-26 00:04:41.591 | INFO | __main__:start_auction:60 - Starting auction for Rare Painting with starting price $1000.0
2025-01-26 00:04:41.654 | INFO | __main__:handle_auction_start:122 - Bob placed bid: $1200.00
2025-01-26 00:04:41.654 | INFO | __main__:handle_auction_start:122 - Alice placed bid: $1500.00
2025-01-26 00:04:41.665 | INFO | __main__:handle_auction_start:122 - Jon placed bid: $2800.00
2025-01-26 00:04:41.669 | INFO | __main__:handle_auction_start:122 - Charlie placed bid: $2000.00
2025-01-26 00:04:41.685 | INFO | __main__:handle_bid:70 - Received bid from Jon for $2800.00
2025-01-26 00:04:41.687 | INFO | __main__:handle_bid:70 - Received bid from Charlie for $2000.00
2025-01-26 00:04:41.695 | INFO | __main__:handle_bid:70 - Received bid from Bob for $1200.00
2025-01-26 00:04:41.695 | INFO | __main__:end_auction:84 - Auction ended. Winner: Jon, Winning Bid: $2800.00
```

3. Add recurring meeting support:
## Customization Options

```python
@dataclass
class Meeting:
recurrence: str # weekly, monthly, etc.
```
- Modify bidding strategy by adjusting the random multiplier range
- Add minimum bid increments
- Implement multiple auction rounds
- Add timeout mechanisms for bidder responses
- Implement different auction types (Dutch, English, etc.)

## Sequence Diagram

````mermaid
sequenceDiagram
participant A as Auctioneer
participant B1 as Bidder1
participant B2 as Bidder2
participant B3 as Bidder3
Note over A,B3: Connection Phase
B1->>A: Connect
A->>B1: Connection Confirmed
B2->>A: Connect
A->>B2: Connection Confirmed
B3->>A: Connect
A->>B3: Connection Confirmed
Note over A,B3: Auction Start
A->>B1: AuctionStart(item)
A->>B2: AuctionStart(item)
A->>B3: AuctionStart(item)
Note over A,B3: Bidding Phase
B1-->>A: Bid(amount)
B2-->>A: Bid(amount)
B3-->>A: Bid(amount)
Note over A,B3: Auction End
A->>B1: AuctionResult(winner, amount)
A->>B2: AuctionResult(winner, amount)
A->>B3: AuctionResult(winner, amount)
A->>B1: AuctionEnd
A->>B2: AuctionEnd
A->>B3: AuctionEnd
````
Loading

0 comments on commit f3f3f5e

Please sign in to comment.