Skip to content

API Reference

Base Class

negmas_llm.LLMNegotiator

Bases: SAONegotiator, ABC

A negotiator that uses an LLM for decision-making.

This negotiator delegates the negotiation strategy to a Large Language Model. It converts negotiation state to text, sends it to the LLM, and parses the response into negotiation actions.

Parameters:

Name Type Description Default
provider str

The LLM provider (e.g., "openai", "anthropic", "ollama").

required
model str

The model name (e.g., "gpt-4", "claude-3-opus").

required
api_key str | None

API key for the provider (if required).

None
api_base str | None

Base URL for the API (useful for local deployments).

None
temperature float

Sampling temperature for the LLM.

0.7
max_tokens int

Maximum tokens in the LLM response.

1024
system_prompt str | None

Custom system prompt (overrides build_system_prompt).

None
llm_kwargs dict[str, Any] | None

Additional keyword arguments passed to litellm.completion.

None
preferences Preferences | None

The preferences of the negotiator.

None
ufun BaseUtilityFunction | None

The utility function (overrides preferences if given).

None
name str | None

Negotiator name.

None
parent Controller | None

Parent Controller if any.

None
owner Agent | None

The Agent that owns the negotiator.

None
id str | None

Unique ID for the negotiator.

None
type_name str | None

Type name string.

None
can_propose bool

Whether the negotiator can propose offers.

True
**kwargs Any

Additional arguments passed to SAONegotiator.

{}
Source code in src/negmas_llm/negotiator.py
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
class LLMNegotiator(SAONegotiator, ABC):
    """A negotiator that uses an LLM for decision-making.

    This negotiator delegates the negotiation strategy to a Large Language Model.
    It converts negotiation state to text, sends it to the LLM, and parses the
    response into negotiation actions.

    Args:
        provider: The LLM provider (e.g., "openai", "anthropic", "ollama").
        model: The model name (e.g., "gpt-4", "claude-3-opus").
        api_key: API key for the provider (if required).
        api_base: Base URL for the API (useful for local deployments).
        temperature: Sampling temperature for the LLM.
        max_tokens: Maximum tokens in the LLM response.
        system_prompt: Custom system prompt (overrides build_system_prompt).
        llm_kwargs: Additional keyword arguments passed to litellm.completion.
        preferences: The preferences of the negotiator.
        ufun: The utility function (overrides preferences if given).
        name: Negotiator name.
        parent: Parent Controller if any.
        owner: The Agent that owns the negotiator.
        id: Unique ID for the negotiator.
        type_name: Type name string.
        can_propose: Whether the negotiator can propose offers.
        **kwargs: Additional arguments passed to SAONegotiator.
    """

    def __init__(
        self,
        provider: str,
        model: str,
        *,
        api_key: str | None = None,
        api_base: str | None = None,
        temperature: float = 0.7,
        max_tokens: int = 1024,
        system_prompt: str | None = None,
        llm_kwargs: dict[str, Any] | None = None,
        preferences: Preferences | None = None,
        ufun: BaseUtilityFunction | None = None,
        name: str | None = None,
        parent: Controller | None = None,
        owner: Agent | None = None,
        id: str | None = None,
        type_name: str | None = None,
        can_propose: bool = True,
        **kwargs: Any,
    ) -> None:
        super().__init__(
            preferences=preferences,
            ufun=ufun,
            name=name,
            parent=parent,
            owner=owner,
            id=id,
            type_name=type_name,
            can_propose=can_propose,
            **kwargs,
        )
        self.provider = provider
        self.model = model
        self.api_key = api_key
        self.api_base = api_base
        self.temperature = temperature
        self.max_tokens = max_tokens
        self._custom_system_prompt = system_prompt
        self.llm_kwargs = llm_kwargs or {}

        # Conversation history for context
        self._conversation_history: list[dict[str, str]] = []

    def get_model_string(self) -> str:
        """Get the model string for litellm.

        Returns:
            The full model string in litellm format (provider/model).
        """
        return f"{self.provider}/{self.model}"

    def format_outcome_space(self, state: SAOState) -> str:
        """Format the outcome space for the LLM.

        Override this method to customize how the outcome space (negotiation
        issues and their possible values) is presented to the LLM.

        Args:
            state: The current negotiation state.

        Returns:
            A string describing the outcome space, or empty string if unavailable.
        """
        if self.nmi is None or self.nmi.outcome_space is None:
            return ""

        outcome_space = self.nmi.outcome_space
        try:
            # Use serialize() for structured representation
            os_dict = serialize(outcome_space)
            # Remove internal class identifier for cleaner output
            os_dict.pop("__python_class__", None)

            parts = ["## Outcome Space"]
            parts.append("")
            parts.append(
                "The negotiation outcome space defines the possible agreements:"
            )
            parts.append("")
            parts.append(f"```json\n{json.dumps(os_dict, indent=2, default=str)}\n```")
            parts.append("")
            parts.append(
                "Each outcome is a tuple of values, one for each issue/dimension."
            )
            return "\n".join(parts)
        except Exception:
            # Fallback to string representation
            return f"## Outcome Space\n\n{outcome_space}\n"

    def format_own_ufun(self, state: SAOState) -> str:
        """Format your own utility function for the LLM.

        Override this method to customize how your utility function is
        presented to the LLM.

        Args:
            state: The current negotiation state.

        Returns:
            A string describing your utility function, or a note if unavailable.
        """
        if self.ufun is None:
            return """## Your Utility Function

You do NOT have a utility function. You must negotiate based on general
principles and any instructions provided.
"""

        try:
            # Get structured representation
            ufun_dict = serialize(self.ufun)
            # Remove internal class identifier
            ufun_dict.pop("__python_class__", None)

            # Get reserved value (utility of no agreement)
            reserved = self.reserved_value

            # Also include string representation for readability
            ufun_str = str(self.ufun)

            parts = ["## Your Utility Function"]
            parts.append("")
            parts.append(
                "You have a utility function that evaluates outcomes. "
                "Higher utility values are better for you."
            )
            parts.append("")
            parts.append(f"**Description**: {ufun_str}")
            parts.append("")
            parts.append(f"**Reserved Value** (utility if no agreement): {reserved}")
            parts.append("")
            parts.append("**Full specification**:")
            parts.append(
                f"```json\n{json.dumps(ufun_dict, indent=2, default=str)}\n```"
            )

            return "\n".join(parts)
        except Exception:
            # Fallback to string representation
            return f"""## Your Utility Function

You have a utility function: {self.ufun}
Reserved value (utility if no agreement): {self.reserved_value}
"""

    def format_partner_ufun(self, state: SAOState) -> str:
        """Format the partner's utility function for the LLM.

        Override this method to customize how the partner's utility function
        (if known) is presented to the LLM.

        Args:
            state: The current negotiation state.

        Returns:
            A string describing partner's utility function, or empty if unknown.
        """
        # Access partner's ufun from private_info (negmas convention)
        partner_ufun = (
            self.private_info.get("opponent_ufun") if self.private_info else None
        )

        if partner_ufun is None:
            return """## Partner's Utility Function

You do NOT know your partner's utility function. You must infer their
preferences from their offers and behavior during the negotiation.
"""

        try:
            # Get structured representation
            ufun_dict = serialize(partner_ufun)
            # Remove internal class identifier
            ufun_dict.pop("__python_class__", None)

            # Get reserved value if available
            reserved = getattr(partner_ufun, "reserved_value", None)

            # Also include string representation for readability
            ufun_str = str(partner_ufun)

            parts = ["## Partner's Utility Function (Known)"]
            parts.append("")
            parts.append(
                "You know your partner's utility function. Use this information "
                "strategically to find mutually beneficial outcomes."
            )
            parts.append("")
            parts.append(f"**Description**: {ufun_str}")
            if reserved is not None:
                parts.append("")
                parts.append(f"**Their Reserved Value**: {reserved}")
            parts.append("")
            parts.append("**Full specification**:")
            parts.append(
                f"```json\n{json.dumps(ufun_dict, indent=2, default=str)}\n```"
            )

            return "\n".join(parts)
        except Exception:
            # Fallback to string representation
            return f"""## Partner's Utility Function (Known)

Your partner's utility function: {partner_ufun}
"""

    def format_nmi_info(self) -> str:
        """Format the Negotiator Mechanism Interface (NMI) information.

        The NMI contains metadata about the negotiation mechanism including
        time limits, step limits, number of possible outcomes, etc.

        Override this method to customize how NMI info is presented to the LLM.

        Returns:
            A string describing the negotiation mechanism parameters.
        """
        if self.nmi is None:
            return ""

        parts = ["## Negotiation Mechanism Information"]
        parts.append("")

        # Number of negotiation steps
        n_steps = self.nmi.n_steps
        if n_steps is not None:
            parts.append(f"- **Maximum steps**: {n_steps}")
        else:
            parts.append("- **Maximum steps**: Unlimited")

        # Time limit
        time_limit = self.nmi.time_limit
        if time_limit is not None:
            parts.append(f"- **Time limit**: {time_limit:.2f} seconds")
        else:
            parts.append("- **Time limit**: Unlimited")

        # Number of possible outcomes
        n_outcomes = self.nmi.n_outcomes
        if n_outcomes is not None:
            parts.append(f"- **Total possible outcomes**: {n_outcomes}")

        # Number of negotiators
        n_negotiators = self.nmi.n_negotiators
        if n_negotiators is not None:
            parts.append(f"- **Number of negotiators**: {n_negotiators}")

        # Dynamic entry allowed
        if hasattr(self.nmi, "dynamic_entry") and self.nmi.dynamic_entry is not None:
            parts.append(f"- **Dynamic entry allowed**: {self.nmi.dynamic_entry}")

        # Annotation space info if available
        if hasattr(self.nmi, "annotation") and self.nmi.annotation:
            parts.append(f"- **Mechanism annotation**: {self.nmi.annotation}")

        parts.append("")
        return "\n".join(parts)

    def format_state(self, state: SAOState, offer: Outcome | None = None) -> str:
        """Format the complete SAOState for the LLM.

        This provides a comprehensive view of the current negotiation state
        including all relevant fields.

        Override this method to customize how state is presented to the LLM.

        Args:
            state: The current negotiation state.
            offer: The specific offer being responded to (may differ from
                current_offer).

        Returns:
            A string describing the complete state.
        """
        parts = ["## Current State"]
        parts.append("")

        # Core timing info
        parts.append(f"- **Step**: {state.step}")
        parts.append(f"- **Relative time**: {state.relative_time:.2%}")

        # Current offer info (use passed offer if provided, else state.current_offer)
        display_offer = offer if offer is not None else state.current_offer
        if display_offer is not None:
            offer_str = self._format_outcome(display_offer)
            parts.append(f"- **Current offer on table**: {offer_str}")
            if state.current_proposer:
                parts.append(f"- **Current proposer**: {state.current_proposer}")

            # Compute utilities if available
            if self.ufun is not None:
                utility = self.ufun(display_offer)
                parts.append(f"- **Your utility for current offer**: {utility:.4f}")

            partner_ufun = (
                self.private_info.get("opponent_ufun") if self.private_info else None
            )
            if partner_ufun is not None:
                try:
                    partner_utility = partner_ufun(display_offer)
                    parts.append(
                        f"- **Partner's utility for current offer**: "
                        f"{partner_utility:.4f}"
                    )
                except Exception:
                    pass
        else:
            parts.append("- **Current offer on table**: None")

        # Number of acceptances so far
        parts.append(f"- **Number of acceptances**: {state.n_acceptances}")

        # Negotiation status flags
        parts.append(f"- **Negotiation running**: {state.running}")
        if state.broken:
            parts.append("- **Status**: BROKEN (negotiation ended abnormally)")
        if state.timedout:
            parts.append("- **Status**: TIMED OUT")
        if state.agreement is not None:
            agreement_str = self._format_outcome(state.agreement)
            parts.append(f"- **Agreement reached**: {agreement_str}")

        # Offer history if available
        if hasattr(state, "new_offers") and state.new_offers:
            parts.append("")
            parts.append("**Recent offers this step**:")
            for proposer, prop_offer in state.new_offers:
                if prop_offer is not None:
                    offer_str = self._format_outcome(prop_offer)
                    parts.append(f"  - {proposer}: {offer_str}")
                else:
                    parts.append(f"  - {proposer}: None")

        parts.append("")
        return "\n".join(parts)

    def format_response_instructions(self) -> str:
        """Format the response format instructions for the LLM.

        Override this method to customize the expected response format.

        Returns:
            A string describing the expected JSON response format.
        """
        return """\
## Response Format

IMPORTANT: Your response MUST be valid JSON in the following format:
{
    "response_type": "accept" | "reject" | "end",
    "outcome": [value1, value2, ...] | null,
    "text": "optional explanation or message to other party",
    "data": {} | null
}

Where:
- "response_type": Your decision:
  - "accept": Accept the current offer
  - "reject": Reject and provide a counter-offer
  - "end": End the negotiation without agreement
- "outcome": If rejecting, provide your counter-offer as a list of values
  matching the issue order. If accepting or ending, this can be null.
- "text": Optional text message or explanation to the other party
- "data": Optional additional data as a dictionary

Always respond with ONLY the JSON object, no additional text."""

    def build_system_prompt(self, state: SAOState) -> str:
        """Build the system prompt for the LLM.

        This method combines the output of format_outcome_space(), format_nmi_info(),
        format_own_ufun(), format_partner_ufun(), and format_response_instructions()
        to create the full system prompt.

        Override this method for complete control, or override the individual
        format_* methods to customize specific sections.

        Args:
            state: The current negotiation state.

        Returns:
            The system prompt string.
        """
        if self._custom_system_prompt:
            return self._custom_system_prompt

        outcome_space_info = self.format_outcome_space(state)
        nmi_info = self.format_nmi_info()
        own_ufun_info = self.format_own_ufun(state)
        partner_ufun_info = self.format_partner_ufun(state)
        response_instructions = self.format_response_instructions()

        return f"""\
You are a skilled negotiator participating in an automated negotiation.
Your goal is to negotiate effectively to achieve good outcomes for yourself
while finding mutually acceptable agreements when possible.

{outcome_space_info}
{nmi_info}
{own_ufun_info}
{partner_ufun_info}
{response_instructions}"""

    def build_user_message(
        self,
        state: SAOState,
        offer: Outcome | None,
        source: str | None,
    ) -> str:
        """Build the user message describing the current negotiation state.

        This method uses format_state() to provide comprehensive state information
        to the LLM for each round.

        Override this method to customize how negotiation state is presented
        to the LLM.

        Args:
            state: The current negotiation state.
            offer: The received offer (if any).
            source: The ID of the negotiator who made the offer.

        Returns:
            The user message string.
        """
        parts = [f"# Negotiation Round {state.step}"]
        parts.append("")

        # Include complete state information
        state_info = self.format_state(state, offer)
        parts.append(state_info)

        # Add context about received offer or first proposal
        if offer is not None:
            parts.append(
                f"You received an offer from **{source or 'opponent'}**. "
                "Please analyze it and respond."
            )
        else:
            parts.append("No offer received yet. Please make the first proposal.")

        parts.append("")
        parts.append("Respond with your decision in JSON format.")

        return "\n".join(parts)

    def _format_outcome(self, outcome: Outcome) -> str:
        """Format an outcome for display.

        Args:
            outcome: The outcome to format.

        Returns:
            A formatted string representation.
        """
        if self.nmi is not None and self.nmi.outcome_space is not None:
            outcome_space = self.nmi.outcome_space
            # Try to get issue names from outcome_space
            try:
                issues = outcome_space.issues  # type: ignore[attr-defined]
                if issues:
                    parts = []
                    for i, value in enumerate(outcome):
                        if i < len(issues):
                            parts.append(f"{issues[i].name}={value}")
                        else:
                            parts.append(str(value))
                    return "{" + ", ".join(parts) + "}"
            except AttributeError:
                pass
        return str(outcome)

    def _call_llm(self, messages: list[dict[str, str]]) -> str:
        """Call the LLM and get a response.

        Args:
            messages: The conversation messages.

        Returns:
            The LLM response text.
        """
        kwargs: dict[str, Any] = {
            "model": self.get_model_string(),
            "messages": messages,
            "temperature": self.temperature,
            "max_tokens": self.max_tokens,
            **self.llm_kwargs,
        }

        if self.api_key:
            kwargs["api_key"] = self.api_key
        if self.api_base:
            kwargs["api_base"] = self.api_base

        response = litellm.completion(**kwargs)
        # Cast to ModelResponse as litellm.completion returns
        # Union[ModelResponse, CustomStreamWrapper] when stream=False (default)
        model_response = cast(ModelResponse, response)
        choices = cast(list["Choices"], model_response.choices)
        return choices[0].message.content or ""

    def _parse_llm_response(
        self, response_text: str, state: SAOState
    ) -> tuple[ResponseType, Outcome | None, str | None, dict[str, Any] | None]:
        """Parse the LLM response into negotiation actions.

        Args:
            response_text: The raw LLM response.
            state: The current negotiation state.

        Returns:
            A tuple of (response_type, outcome, text, data).
        """
        # Try to extract JSON from the response
        json_match = re.search(r"\{[\s\S]*\}", response_text)
        if not json_match:
            # Default to reject with no counter if parsing fails
            return ResponseType.REJECT_OFFER, None, None, None

        try:
            data = json.loads(json_match.group())
        except json.JSONDecodeError:
            return ResponseType.REJECT_OFFER, None, None, None

        # Parse response type
        response_type_str = data.get("response_type", "reject").lower()
        response_type_map = {
            "accept": ResponseType.ACCEPT_OFFER,
            "reject": ResponseType.REJECT_OFFER,
            "end": ResponseType.END_NEGOTIATION,
        }
        response_type = response_type_map.get(
            response_type_str, ResponseType.REJECT_OFFER
        )

        # Parse outcome
        outcome: Outcome | None = None
        outcome_data = data.get("outcome")
        if outcome_data is not None and isinstance(outcome_data, list):
            outcome = tuple(outcome_data)

        # Parse text and additional data
        text = data.get("text")
        extra_data = data.get("data")

        return response_type, outcome, text, extra_data

    def counter(
        self,
        state: SAOState,
        offer: Outcome | None,
        source: str | None = None,
        dest: str | None = None,
    ) -> SAOResponse:
        """Generate a counter-offer using the LLM.

        This method is called by the negotiation mechanism when this negotiator
        needs to respond to an offer.

        Args:
            state: The current negotiation state.
            offer: The received offer (or None if this is the first proposal).
            source: The ID of the negotiator who made the offer.
            dest: The ID of the destination negotiator.

        Returns:
            An SAOResponse containing the response type and optional counter-offer.
        """
        # Build messages
        system_prompt = self.build_system_prompt(state)
        user_message = self.build_user_message(state, offer, source)

        messages = [
            {"role": "system", "content": system_prompt},
            *self._conversation_history,
            {"role": "user", "content": user_message},
        ]

        # Call LLM
        response_text = self._call_llm(messages)

        # Update conversation history
        self._conversation_history.append({"role": "user", "content": user_message})
        self._conversation_history.append(
            {"role": "assistant", "content": response_text}
        )

        # Parse response
        response_type, outcome, text, extra_data = self._parse_llm_response(
            response_text, state
        )

        # Build response data
        response_data: dict[str, Any] | None = None
        if text or extra_data:
            response_data = {}
            if text:
                response_data["text"] = text
            if extra_data:
                response_data.update(extra_data)

        return SAOResponse(
            response=response_type,
            outcome=outcome,
            data=response_data,
        )

    def propose(
        self, state: SAOState, dest: str | None = None
    ) -> Outcome | ExtendedOutcome | None:
        """Propose an offer using the LLM.

        Args:
            state: The current negotiation state.
            dest: The ID of the destination negotiator.

        Returns:
            The proposed outcome or None.
        """
        response = self.counter(state, offer=None, source=None, dest=dest)
        if response.outcome is not None:
            if response.data:
                return ExtendedOutcome(outcome=response.outcome, data=response.data)
            return response.outcome
        return None

    def respond(self, state: SAOState, source: str | None = None) -> ResponseType:
        """Respond to an offer using the LLM.

        Args:
            state: The current negotiation state.
            source: The ID of the negotiator who made the offer.

        Returns:
            The response type.
        """
        response = self.counter(state, offer=state.current_offer, source=source)
        return response.response

    def on_negotiation_start(self, state: GBState) -> None:
        """Reset conversation history when negotiation starts."""
        super().on_negotiation_start(state)
        self._conversation_history = []

Functions

__init__(provider, model, *, api_key=None, api_base=None, temperature=0.7, max_tokens=1024, system_prompt=None, llm_kwargs=None, preferences=None, ufun=None, name=None, parent=None, owner=None, id=None, type_name=None, can_propose=True, **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    provider: str,
    model: str,
    *,
    api_key: str | None = None,
    api_base: str | None = None,
    temperature: float = 0.7,
    max_tokens: int = 1024,
    system_prompt: str | None = None,
    llm_kwargs: dict[str, Any] | None = None,
    preferences: Preferences | None = None,
    ufun: BaseUtilityFunction | None = None,
    name: str | None = None,
    parent: Controller | None = None,
    owner: Agent | None = None,
    id: str | None = None,
    type_name: str | None = None,
    can_propose: bool = True,
    **kwargs: Any,
) -> None:
    super().__init__(
        preferences=preferences,
        ufun=ufun,
        name=name,
        parent=parent,
        owner=owner,
        id=id,
        type_name=type_name,
        can_propose=can_propose,
        **kwargs,
    )
    self.provider = provider
    self.model = model
    self.api_key = api_key
    self.api_base = api_base
    self.temperature = temperature
    self.max_tokens = max_tokens
    self._custom_system_prompt = system_prompt
    self.llm_kwargs = llm_kwargs or {}

    # Conversation history for context
    self._conversation_history: list[dict[str, str]] = []

get_model_string()

Get the model string for litellm.

Returns:

Type Description
str

The full model string in litellm format (provider/model).

Source code in src/negmas_llm/negotiator.py
def get_model_string(self) -> str:
    """Get the model string for litellm.

    Returns:
        The full model string in litellm format (provider/model).
    """
    return f"{self.provider}/{self.model}"

format_outcome_space(state)

Format the outcome space for the LLM.

Override this method to customize how the outcome space (negotiation issues and their possible values) is presented to the LLM.

Parameters:

Name Type Description Default
state SAOState

The current negotiation state.

required

Returns:

Type Description
str

A string describing the outcome space, or empty string if unavailable.

Source code in src/negmas_llm/negotiator.py
def format_outcome_space(self, state: SAOState) -> str:
    """Format the outcome space for the LLM.

    Override this method to customize how the outcome space (negotiation
    issues and their possible values) is presented to the LLM.

    Args:
        state: The current negotiation state.

    Returns:
        A string describing the outcome space, or empty string if unavailable.
    """
    if self.nmi is None or self.nmi.outcome_space is None:
        return ""

    outcome_space = self.nmi.outcome_space
    try:
        # Use serialize() for structured representation
        os_dict = serialize(outcome_space)
        # Remove internal class identifier for cleaner output
        os_dict.pop("__python_class__", None)

        parts = ["## Outcome Space"]
        parts.append("")
        parts.append(
            "The negotiation outcome space defines the possible agreements:"
        )
        parts.append("")
        parts.append(f"```json\n{json.dumps(os_dict, indent=2, default=str)}\n```")
        parts.append("")
        parts.append(
            "Each outcome is a tuple of values, one for each issue/dimension."
        )
        return "\n".join(parts)
    except Exception:
        # Fallback to string representation
        return f"## Outcome Space\n\n{outcome_space}\n"

format_own_ufun(state)

Format your own utility function for the LLM.

Override this method to customize how your utility function is presented to the LLM.

Parameters:

Name Type Description Default
state SAOState

The current negotiation state.

required

Returns:

Type Description
str

A string describing your utility function, or a note if unavailable.

Source code in src/negmas_llm/negotiator.py
    def format_own_ufun(self, state: SAOState) -> str:
        """Format your own utility function for the LLM.

        Override this method to customize how your utility function is
        presented to the LLM.

        Args:
            state: The current negotiation state.

        Returns:
            A string describing your utility function, or a note if unavailable.
        """
        if self.ufun is None:
            return """## Your Utility Function

You do NOT have a utility function. You must negotiate based on general
principles and any instructions provided.
"""

        try:
            # Get structured representation
            ufun_dict = serialize(self.ufun)
            # Remove internal class identifier
            ufun_dict.pop("__python_class__", None)

            # Get reserved value (utility of no agreement)
            reserved = self.reserved_value

            # Also include string representation for readability
            ufun_str = str(self.ufun)

            parts = ["## Your Utility Function"]
            parts.append("")
            parts.append(
                "You have a utility function that evaluates outcomes. "
                "Higher utility values are better for you."
            )
            parts.append("")
            parts.append(f"**Description**: {ufun_str}")
            parts.append("")
            parts.append(f"**Reserved Value** (utility if no agreement): {reserved}")
            parts.append("")
            parts.append("**Full specification**:")
            parts.append(
                f"```json\n{json.dumps(ufun_dict, indent=2, default=str)}\n```"
            )

            return "\n".join(parts)
        except Exception:
            # Fallback to string representation
            return f"""## Your Utility Function

You have a utility function: {self.ufun}
Reserved value (utility if no agreement): {self.reserved_value}
"""

format_partner_ufun(state)

Format the partner's utility function for the LLM.

Override this method to customize how the partner's utility function (if known) is presented to the LLM.

Parameters:

Name Type Description Default
state SAOState

The current negotiation state.

required

Returns:

Type Description
str

A string describing partner's utility function, or empty if unknown.

Source code in src/negmas_llm/negotiator.py
    def format_partner_ufun(self, state: SAOState) -> str:
        """Format the partner's utility function for the LLM.

        Override this method to customize how the partner's utility function
        (if known) is presented to the LLM.

        Args:
            state: The current negotiation state.

        Returns:
            A string describing partner's utility function, or empty if unknown.
        """
        # Access partner's ufun from private_info (negmas convention)
        partner_ufun = (
            self.private_info.get("opponent_ufun") if self.private_info else None
        )

        if partner_ufun is None:
            return """## Partner's Utility Function

You do NOT know your partner's utility function. You must infer their
preferences from their offers and behavior during the negotiation.
"""

        try:
            # Get structured representation
            ufun_dict = serialize(partner_ufun)
            # Remove internal class identifier
            ufun_dict.pop("__python_class__", None)

            # Get reserved value if available
            reserved = getattr(partner_ufun, "reserved_value", None)

            # Also include string representation for readability
            ufun_str = str(partner_ufun)

            parts = ["## Partner's Utility Function (Known)"]
            parts.append("")
            parts.append(
                "You know your partner's utility function. Use this information "
                "strategically to find mutually beneficial outcomes."
            )
            parts.append("")
            parts.append(f"**Description**: {ufun_str}")
            if reserved is not None:
                parts.append("")
                parts.append(f"**Their Reserved Value**: {reserved}")
            parts.append("")
            parts.append("**Full specification**:")
            parts.append(
                f"```json\n{json.dumps(ufun_dict, indent=2, default=str)}\n```"
            )

            return "\n".join(parts)
        except Exception:
            # Fallback to string representation
            return f"""## Partner's Utility Function (Known)

Your partner's utility function: {partner_ufun}
"""

format_nmi_info()

Format the Negotiator Mechanism Interface (NMI) information.

The NMI contains metadata about the negotiation mechanism including time limits, step limits, number of possible outcomes, etc.

Override this method to customize how NMI info is presented to the LLM.

Returns:

Type Description
str

A string describing the negotiation mechanism parameters.

Source code in src/negmas_llm/negotiator.py
def format_nmi_info(self) -> str:
    """Format the Negotiator Mechanism Interface (NMI) information.

    The NMI contains metadata about the negotiation mechanism including
    time limits, step limits, number of possible outcomes, etc.

    Override this method to customize how NMI info is presented to the LLM.

    Returns:
        A string describing the negotiation mechanism parameters.
    """
    if self.nmi is None:
        return ""

    parts = ["## Negotiation Mechanism Information"]
    parts.append("")

    # Number of negotiation steps
    n_steps = self.nmi.n_steps
    if n_steps is not None:
        parts.append(f"- **Maximum steps**: {n_steps}")
    else:
        parts.append("- **Maximum steps**: Unlimited")

    # Time limit
    time_limit = self.nmi.time_limit
    if time_limit is not None:
        parts.append(f"- **Time limit**: {time_limit:.2f} seconds")
    else:
        parts.append("- **Time limit**: Unlimited")

    # Number of possible outcomes
    n_outcomes = self.nmi.n_outcomes
    if n_outcomes is not None:
        parts.append(f"- **Total possible outcomes**: {n_outcomes}")

    # Number of negotiators
    n_negotiators = self.nmi.n_negotiators
    if n_negotiators is not None:
        parts.append(f"- **Number of negotiators**: {n_negotiators}")

    # Dynamic entry allowed
    if hasattr(self.nmi, "dynamic_entry") and self.nmi.dynamic_entry is not None:
        parts.append(f"- **Dynamic entry allowed**: {self.nmi.dynamic_entry}")

    # Annotation space info if available
    if hasattr(self.nmi, "annotation") and self.nmi.annotation:
        parts.append(f"- **Mechanism annotation**: {self.nmi.annotation}")

    parts.append("")
    return "\n".join(parts)

format_state(state, offer=None)

Format the complete SAOState for the LLM.

This provides a comprehensive view of the current negotiation state including all relevant fields.

Override this method to customize how state is presented to the LLM.

Parameters:

Name Type Description Default
state SAOState

The current negotiation state.

required
offer Outcome | None

The specific offer being responded to (may differ from current_offer).

None

Returns:

Type Description
str

A string describing the complete state.

Source code in src/negmas_llm/negotiator.py
def format_state(self, state: SAOState, offer: Outcome | None = None) -> str:
    """Format the complete SAOState for the LLM.

    This provides a comprehensive view of the current negotiation state
    including all relevant fields.

    Override this method to customize how state is presented to the LLM.

    Args:
        state: The current negotiation state.
        offer: The specific offer being responded to (may differ from
            current_offer).

    Returns:
        A string describing the complete state.
    """
    parts = ["## Current State"]
    parts.append("")

    # Core timing info
    parts.append(f"- **Step**: {state.step}")
    parts.append(f"- **Relative time**: {state.relative_time:.2%}")

    # Current offer info (use passed offer if provided, else state.current_offer)
    display_offer = offer if offer is not None else state.current_offer
    if display_offer is not None:
        offer_str = self._format_outcome(display_offer)
        parts.append(f"- **Current offer on table**: {offer_str}")
        if state.current_proposer:
            parts.append(f"- **Current proposer**: {state.current_proposer}")

        # Compute utilities if available
        if self.ufun is not None:
            utility = self.ufun(display_offer)
            parts.append(f"- **Your utility for current offer**: {utility:.4f}")

        partner_ufun = (
            self.private_info.get("opponent_ufun") if self.private_info else None
        )
        if partner_ufun is not None:
            try:
                partner_utility = partner_ufun(display_offer)
                parts.append(
                    f"- **Partner's utility for current offer**: "
                    f"{partner_utility:.4f}"
                )
            except Exception:
                pass
    else:
        parts.append("- **Current offer on table**: None")

    # Number of acceptances so far
    parts.append(f"- **Number of acceptances**: {state.n_acceptances}")

    # Negotiation status flags
    parts.append(f"- **Negotiation running**: {state.running}")
    if state.broken:
        parts.append("- **Status**: BROKEN (negotiation ended abnormally)")
    if state.timedout:
        parts.append("- **Status**: TIMED OUT")
    if state.agreement is not None:
        agreement_str = self._format_outcome(state.agreement)
        parts.append(f"- **Agreement reached**: {agreement_str}")

    # Offer history if available
    if hasattr(state, "new_offers") and state.new_offers:
        parts.append("")
        parts.append("**Recent offers this step**:")
        for proposer, prop_offer in state.new_offers:
            if prop_offer is not None:
                offer_str = self._format_outcome(prop_offer)
                parts.append(f"  - {proposer}: {offer_str}")
            else:
                parts.append(f"  - {proposer}: None")

    parts.append("")
    return "\n".join(parts)

format_response_instructions()

Format the response format instructions for the LLM.

Override this method to customize the expected response format.

Returns:

Type Description
str

A string describing the expected JSON response format.

Source code in src/negmas_llm/negotiator.py
    def format_response_instructions(self) -> str:
        """Format the response format instructions for the LLM.

        Override this method to customize the expected response format.

        Returns:
            A string describing the expected JSON response format.
        """
        return """\
## Response Format

IMPORTANT: Your response MUST be valid JSON in the following format:
{
    "response_type": "accept" | "reject" | "end",
    "outcome": [value1, value2, ...] | null,
    "text": "optional explanation or message to other party",
    "data": {} | null
}

Where:
- "response_type": Your decision:
  - "accept": Accept the current offer
  - "reject": Reject and provide a counter-offer
  - "end": End the negotiation without agreement
- "outcome": If rejecting, provide your counter-offer as a list of values
  matching the issue order. If accepting or ending, this can be null.
- "text": Optional text message or explanation to the other party
- "data": Optional additional data as a dictionary

Always respond with ONLY the JSON object, no additional text."""

build_system_prompt(state)

Build the system prompt for the LLM.

This method combines the output of format_outcome_space(), format_nmi_info(), format_own_ufun(), format_partner_ufun(), and format_response_instructions() to create the full system prompt.

Override this method for complete control, or override the individual format_* methods to customize specific sections.

Parameters:

Name Type Description Default
state SAOState

The current negotiation state.

required

Returns:

Type Description
str

The system prompt string.

Source code in src/negmas_llm/negotiator.py
    def build_system_prompt(self, state: SAOState) -> str:
        """Build the system prompt for the LLM.

        This method combines the output of format_outcome_space(), format_nmi_info(),
        format_own_ufun(), format_partner_ufun(), and format_response_instructions()
        to create the full system prompt.

        Override this method for complete control, or override the individual
        format_* methods to customize specific sections.

        Args:
            state: The current negotiation state.

        Returns:
            The system prompt string.
        """
        if self._custom_system_prompt:
            return self._custom_system_prompt

        outcome_space_info = self.format_outcome_space(state)
        nmi_info = self.format_nmi_info()
        own_ufun_info = self.format_own_ufun(state)
        partner_ufun_info = self.format_partner_ufun(state)
        response_instructions = self.format_response_instructions()

        return f"""\
You are a skilled negotiator participating in an automated negotiation.
Your goal is to negotiate effectively to achieve good outcomes for yourself
while finding mutually acceptable agreements when possible.

{outcome_space_info}
{nmi_info}
{own_ufun_info}
{partner_ufun_info}
{response_instructions}"""

build_user_message(state, offer, source)

Build the user message describing the current negotiation state.

This method uses format_state() to provide comprehensive state information to the LLM for each round.

Override this method to customize how negotiation state is presented to the LLM.

Parameters:

Name Type Description Default
state SAOState

The current negotiation state.

required
offer Outcome | None

The received offer (if any).

required
source str | None

The ID of the negotiator who made the offer.

required

Returns:

Type Description
str

The user message string.

Source code in src/negmas_llm/negotiator.py
def build_user_message(
    self,
    state: SAOState,
    offer: Outcome | None,
    source: str | None,
) -> str:
    """Build the user message describing the current negotiation state.

    This method uses format_state() to provide comprehensive state information
    to the LLM for each round.

    Override this method to customize how negotiation state is presented
    to the LLM.

    Args:
        state: The current negotiation state.
        offer: The received offer (if any).
        source: The ID of the negotiator who made the offer.

    Returns:
        The user message string.
    """
    parts = [f"# Negotiation Round {state.step}"]
    parts.append("")

    # Include complete state information
    state_info = self.format_state(state, offer)
    parts.append(state_info)

    # Add context about received offer or first proposal
    if offer is not None:
        parts.append(
            f"You received an offer from **{source or 'opponent'}**. "
            "Please analyze it and respond."
        )
    else:
        parts.append("No offer received yet. Please make the first proposal.")

    parts.append("")
    parts.append("Respond with your decision in JSON format.")

    return "\n".join(parts)

counter(state, offer, source=None, dest=None)

Generate a counter-offer using the LLM.

This method is called by the negotiation mechanism when this negotiator needs to respond to an offer.

Parameters:

Name Type Description Default
state SAOState

The current negotiation state.

required
offer Outcome | None

The received offer (or None if this is the first proposal).

required
source str | None

The ID of the negotiator who made the offer.

None
dest str | None

The ID of the destination negotiator.

None

Returns:

Type Description
SAOResponse

An SAOResponse containing the response type and optional counter-offer.

Source code in src/negmas_llm/negotiator.py
def counter(
    self,
    state: SAOState,
    offer: Outcome | None,
    source: str | None = None,
    dest: str | None = None,
) -> SAOResponse:
    """Generate a counter-offer using the LLM.

    This method is called by the negotiation mechanism when this negotiator
    needs to respond to an offer.

    Args:
        state: The current negotiation state.
        offer: The received offer (or None if this is the first proposal).
        source: The ID of the negotiator who made the offer.
        dest: The ID of the destination negotiator.

    Returns:
        An SAOResponse containing the response type and optional counter-offer.
    """
    # Build messages
    system_prompt = self.build_system_prompt(state)
    user_message = self.build_user_message(state, offer, source)

    messages = [
        {"role": "system", "content": system_prompt},
        *self._conversation_history,
        {"role": "user", "content": user_message},
    ]

    # Call LLM
    response_text = self._call_llm(messages)

    # Update conversation history
    self._conversation_history.append({"role": "user", "content": user_message})
    self._conversation_history.append(
        {"role": "assistant", "content": response_text}
    )

    # Parse response
    response_type, outcome, text, extra_data = self._parse_llm_response(
        response_text, state
    )

    # Build response data
    response_data: dict[str, Any] | None = None
    if text or extra_data:
        response_data = {}
        if text:
            response_data["text"] = text
        if extra_data:
            response_data.update(extra_data)

    return SAOResponse(
        response=response_type,
        outcome=outcome,
        data=response_data,
    )

propose(state, dest=None)

Propose an offer using the LLM.

Parameters:

Name Type Description Default
state SAOState

The current negotiation state.

required
dest str | None

The ID of the destination negotiator.

None

Returns:

Type Description
Outcome | ExtendedOutcome | None

The proposed outcome or None.

Source code in src/negmas_llm/negotiator.py
def propose(
    self, state: SAOState, dest: str | None = None
) -> Outcome | ExtendedOutcome | None:
    """Propose an offer using the LLM.

    Args:
        state: The current negotiation state.
        dest: The ID of the destination negotiator.

    Returns:
        The proposed outcome or None.
    """
    response = self.counter(state, offer=None, source=None, dest=dest)
    if response.outcome is not None:
        if response.data:
            return ExtendedOutcome(outcome=response.outcome, data=response.data)
        return response.outcome
    return None

respond(state, source=None)

Respond to an offer using the LLM.

Parameters:

Name Type Description Default
state SAOState

The current negotiation state.

required
source str | None

The ID of the negotiator who made the offer.

None

Returns:

Type Description
ResponseType

The response type.

Source code in src/negmas_llm/negotiator.py
def respond(self, state: SAOState, source: str | None = None) -> ResponseType:
    """Respond to an offer using the LLM.

    Args:
        state: The current negotiation state.
        source: The ID of the negotiator who made the offer.

    Returns:
        The response type.
    """
    response = self.counter(state, offer=state.current_offer, source=source)
    return response.response

on_negotiation_start(state)

Reset conversation history when negotiation starts.

Source code in src/negmas_llm/negotiator.py
def on_negotiation_start(self, state: GBState) -> None:
    """Reset conversation history when negotiation starts."""
    super().on_negotiation_start(state)
    self._conversation_history = []

Cloud Providers

OpenAI

negmas_llm.OpenAINegotiator

Bases: LLMNegotiator

LLM Negotiator using OpenAI models.

Parameters:

Name Type Description Default
model str

OpenAI model name (default: "gpt-4o").

'gpt-4o'
api_key str | None

OpenAI API key (uses OPENAI_API_KEY env var if not provided).

None
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class OpenAINegotiator(LLMNegotiator):
    """LLM Negotiator using OpenAI models.

    Args:
        model: OpenAI model name (default: "gpt-4o").
        api_key: OpenAI API key (uses OPENAI_API_KEY env var if not provided).
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "gpt-4o",
        *,
        api_key: str | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="openai",
            model=model,
            api_key=api_key,
            **kwargs,
        )

Functions

__init__(model='gpt-4o', *, api_key=None, **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "gpt-4o",
    *,
    api_key: str | None = None,
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="openai",
        model=model,
        api_key=api_key,
        **kwargs,
    )

Anthropic

negmas_llm.AnthropicNegotiator

Bases: LLMNegotiator

LLM Negotiator using Anthropic Claude models.

Parameters:

Name Type Description Default
model str

Anthropic model name (default: "claude-sonnet-4-20250514").

'claude-sonnet-4-20250514'
api_key str | None

Anthropic API key (uses ANTHROPIC_API_KEY env var if not provided).

None
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class AnthropicNegotiator(LLMNegotiator):
    """LLM Negotiator using Anthropic Claude models.

    Args:
        model: Anthropic model name (default: "claude-sonnet-4-20250514").
        api_key: Anthropic API key (uses ANTHROPIC_API_KEY env var if not provided).
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "claude-sonnet-4-20250514",
        *,
        api_key: str | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="anthropic",
            model=model,
            api_key=api_key,
            **kwargs,
        )

Functions

__init__(model='claude-sonnet-4-20250514', *, api_key=None, **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "claude-sonnet-4-20250514",
    *,
    api_key: str | None = None,
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="anthropic",
        model=model,
        api_key=api_key,
        **kwargs,
    )

Google Gemini

negmas_llm.GeminiNegotiator

Bases: LLMNegotiator

LLM Negotiator using Google Gemini models.

Parameters:

Name Type Description Default
model str

Gemini model name (default: "gemini-2.0-flash").

'gemini-2.0-flash'
api_key str | None

Google API key (uses GOOGLE_API_KEY env var if not provided).

None
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class GeminiNegotiator(LLMNegotiator):
    """LLM Negotiator using Google Gemini models.

    Args:
        model: Gemini model name (default: "gemini-2.0-flash").
        api_key: Google API key (uses GOOGLE_API_KEY env var if not provided).
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "gemini-2.0-flash",
        *,
        api_key: str | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="gemini",
            model=model,
            api_key=api_key,
            **kwargs,
        )

Functions

__init__(model='gemini-2.0-flash', *, api_key=None, **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "gemini-2.0-flash",
    *,
    api_key: str | None = None,
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="gemini",
        model=model,
        api_key=api_key,
        **kwargs,
    )

Cohere

negmas_llm.CohereNegotiator

Bases: LLMNegotiator

LLM Negotiator using Cohere models.

Parameters:

Name Type Description Default
model str

Cohere model name (default: "command-r-plus").

'command-r-plus'
api_key str | None

Cohere API key (uses COHERE_API_KEY env var if not provided).

None
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class CohereNegotiator(LLMNegotiator):
    """LLM Negotiator using Cohere models.

    Args:
        model: Cohere model name (default: "command-r-plus").
        api_key: Cohere API key (uses COHERE_API_KEY env var if not provided).
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "command-r-plus",
        *,
        api_key: str | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="cohere",
            model=model,
            api_key=api_key,
            **kwargs,
        )

Functions

__init__(model='command-r-plus', *, api_key=None, **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "command-r-plus",
    *,
    api_key: str | None = None,
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="cohere",
        model=model,
        api_key=api_key,
        **kwargs,
    )

Mistral

negmas_llm.MistralNegotiator

Bases: LLMNegotiator

LLM Negotiator using Mistral AI models.

Parameters:

Name Type Description Default
model str

Mistral model name (default: "mistral-large-latest").

'mistral-large-latest'
api_key str | None

Mistral API key (uses MISTRAL_API_KEY env var if not provided).

None
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class MistralNegotiator(LLMNegotiator):
    """LLM Negotiator using Mistral AI models.

    Args:
        model: Mistral model name (default: "mistral-large-latest").
        api_key: Mistral API key (uses MISTRAL_API_KEY env var if not provided).
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "mistral-large-latest",
        *,
        api_key: str | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="mistral",
            model=model,
            api_key=api_key,
            **kwargs,
        )

Functions

__init__(model='mistral-large-latest', *, api_key=None, **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "mistral-large-latest",
    *,
    api_key: str | None = None,
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="mistral",
        model=model,
        api_key=api_key,
        **kwargs,
    )

Groq

negmas_llm.GroqNegotiator

Bases: LLMNegotiator

LLM Negotiator using Groq-hosted models.

Parameters:

Name Type Description Default
model str

Groq model name (default: "llama-3.3-70b-versatile").

'llama-3.3-70b-versatile'
api_key str | None

Groq API key (uses GROQ_API_KEY env var if not provided).

None
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class GroqNegotiator(LLMNegotiator):
    """LLM Negotiator using Groq-hosted models.

    Args:
        model: Groq model name (default: "llama-3.3-70b-versatile").
        api_key: Groq API key (uses GROQ_API_KEY env var if not provided).
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "llama-3.3-70b-versatile",
        *,
        api_key: str | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="groq",
            model=model,
            api_key=api_key,
            **kwargs,
        )

Functions

__init__(model='llama-3.3-70b-versatile', *, api_key=None, **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "llama-3.3-70b-versatile",
    *,
    api_key: str | None = None,
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="groq",
        model=model,
        api_key=api_key,
        **kwargs,
    )

Together AI

negmas_llm.TogetherAINegotiator

Bases: LLMNegotiator

LLM Negotiator using Together AI hosted models.

Parameters:

Name Type Description Default
model str

Together AI model name (default: "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo").

'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo'
api_key str | None

Together AI API key (uses TOGETHER_API_KEY env var if not provided).

None
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class TogetherAINegotiator(LLMNegotiator):
    """LLM Negotiator using Together AI hosted models.

    Args:
        model: Together AI model name
            (default: "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo").
        api_key: Together AI API key (uses TOGETHER_API_KEY env var if not provided).
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
        *,
        api_key: str | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="together_ai",
            model=model,
            api_key=api_key,
            **kwargs,
        )

Functions

__init__(model='meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo', *, api_key=None, **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
    *,
    api_key: str | None = None,
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="together_ai",
        model=model,
        api_key=api_key,
        **kwargs,
    )

Azure OpenAI

negmas_llm.AzureOpenAINegotiator

Bases: LLMNegotiator

LLM Negotiator using Azure OpenAI Service.

Parameters:

Name Type Description Default
model str

Azure deployment name.

required
api_key str | None

Azure OpenAI API key.

None
api_base str | None

Azure OpenAI endpoint URL.

None
api_version str

Azure OpenAI API version (default: "2024-02-15-preview").

'2024-02-15-preview'
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class AzureOpenAINegotiator(LLMNegotiator):
    """LLM Negotiator using Azure OpenAI Service.

    Args:
        model: Azure deployment name.
        api_key: Azure OpenAI API key.
        api_base: Azure OpenAI endpoint URL.
        api_version: Azure OpenAI API version (default: "2024-02-15-preview").
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str,
        *,
        api_key: str | None = None,
        api_base: str | None = None,
        api_version: str = "2024-02-15-preview",
        **kwargs: Any,
    ) -> None:
        llm_kwargs = kwargs.pop("llm_kwargs", {}) or {}
        llm_kwargs["api_version"] = api_version
        super().__init__(
            provider="azure",
            model=model,
            api_key=api_key,
            api_base=api_base,
            llm_kwargs=llm_kwargs,
            **kwargs,
        )

Functions

__init__(model, *, api_key=None, api_base=None, api_version='2024-02-15-preview', **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str,
    *,
    api_key: str | None = None,
    api_base: str | None = None,
    api_version: str = "2024-02-15-preview",
    **kwargs: Any,
) -> None:
    llm_kwargs = kwargs.pop("llm_kwargs", {}) or {}
    llm_kwargs["api_version"] = api_version
    super().__init__(
        provider="azure",
        model=model,
        api_key=api_key,
        api_base=api_base,
        llm_kwargs=llm_kwargs,
        **kwargs,
    )

AWS Bedrock

negmas_llm.AWSBedrockNegotiator

Bases: LLMNegotiator

LLM Negotiator using AWS Bedrock.

Parameters:

Name Type Description Default
model str

Bedrock model ID (default: "anthropic.claude-3-sonnet-20240229-v1:0").

'anthropic.claude-3-sonnet-20240229-v1:0'
aws_region str

AWS region (default: "us-east-1").

'us-east-1'
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class AWSBedrockNegotiator(LLMNegotiator):
    """LLM Negotiator using AWS Bedrock.

    Args:
        model: Bedrock model ID (default: "anthropic.claude-3-sonnet-20240229-v1:0").
        aws_region: AWS region (default: "us-east-1").
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "anthropic.claude-3-sonnet-20240229-v1:0",
        *,
        aws_region: str = "us-east-1",
        **kwargs: Any,
    ) -> None:
        llm_kwargs = kwargs.pop("llm_kwargs", {}) or {}
        llm_kwargs["aws_region_name"] = aws_region
        super().__init__(
            provider="bedrock",
            model=model,
            llm_kwargs=llm_kwargs,
            **kwargs,
        )

Functions

__init__(model='anthropic.claude-3-sonnet-20240229-v1:0', *, aws_region='us-east-1', **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "anthropic.claude-3-sonnet-20240229-v1:0",
    *,
    aws_region: str = "us-east-1",
    **kwargs: Any,
) -> None:
    llm_kwargs = kwargs.pop("llm_kwargs", {}) or {}
    llm_kwargs["aws_region_name"] = aws_region
    super().__init__(
        provider="bedrock",
        model=model,
        llm_kwargs=llm_kwargs,
        **kwargs,
    )

OpenRouter

negmas_llm.OpenRouterNegotiator

Bases: LLMNegotiator

LLM Negotiator using OpenRouter API.

OpenRouter provides access to many models through a unified API.

Parameters:

Name Type Description Default
model str

OpenRouter model name (default: "openai/gpt-4o").

'openai/gpt-4o'
api_key str | None

OpenRouter API key (uses OPENROUTER_API_KEY env var if not provided).

None
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class OpenRouterNegotiator(LLMNegotiator):
    """LLM Negotiator using OpenRouter API.

    OpenRouter provides access to many models through a unified API.

    Args:
        model: OpenRouter model name (default: "openai/gpt-4o").
        api_key: OpenRouter API key (uses OPENROUTER_API_KEY env var if not provided).
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "openai/gpt-4o",
        *,
        api_key: str | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="openrouter",
            model=model,
            api_key=api_key,
            **kwargs,
        )

Functions

__init__(model='openai/gpt-4o', *, api_key=None, **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "openai/gpt-4o",
    *,
    api_key: str | None = None,
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="openrouter",
        model=model,
        api_key=api_key,
        **kwargs,
    )

DeepSeek

negmas_llm.DeepSeekNegotiator

Bases: LLMNegotiator

LLM Negotiator using DeepSeek models.

Parameters:

Name Type Description Default
model str

DeepSeek model name (default: "deepseek-chat").

'deepseek-chat'
api_key str | None

DeepSeek API key (uses DEEPSEEK_API_KEY env var if not provided).

None
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class DeepSeekNegotiator(LLMNegotiator):
    """LLM Negotiator using DeepSeek models.

    Args:
        model: DeepSeek model name (default: "deepseek-chat").
        api_key: DeepSeek API key (uses DEEPSEEK_API_KEY env var if not provided).
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "deepseek-chat",
        *,
        api_key: str | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="deepseek",
            model=model,
            api_key=api_key,
            **kwargs,
        )

Functions

__init__(model='deepseek-chat', *, api_key=None, **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "deepseek-chat",
    *,
    api_key: str | None = None,
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="deepseek",
        model=model,
        api_key=api_key,
        **kwargs,
    )

Hugging Face

negmas_llm.HuggingFaceNegotiator

Bases: LLMNegotiator

LLM Negotiator using Hugging Face Inference API.

Parameters:

Name Type Description Default
model str

Hugging Face model ID (default: "meta-llama/Llama-3.2-3B-Instruct").

'meta-llama/Llama-3.2-3B-Instruct'
api_key str | None

Hugging Face API token (uses HF_TOKEN env var if not provided).

None
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class HuggingFaceNegotiator(LLMNegotiator):
    """LLM Negotiator using Hugging Face Inference API.

    Args:
        model: Hugging Face model ID (default: "meta-llama/Llama-3.2-3B-Instruct").
        api_key: Hugging Face API token (uses HF_TOKEN env var if not provided).
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "meta-llama/Llama-3.2-3B-Instruct",
        *,
        api_key: str | None = None,
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="huggingface",
            model=model,
            api_key=api_key,
            **kwargs,
        )

Functions

__init__(model='meta-llama/Llama-3.2-3B-Instruct', *, api_key=None, **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "meta-llama/Llama-3.2-3B-Instruct",
    *,
    api_key: str | None = None,
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="huggingface",
        model=model,
        api_key=api_key,
        **kwargs,
    )

Local Providers

Ollama

negmas_llm.OllamaNegotiator

Bases: LLMNegotiator

LLM Negotiator using Ollama for local model inference.

Parameters:

Name Type Description Default
model str

Ollama model name (default: "llama3.2").

'llama3.2'
api_base str

Ollama server URL (default: "http://localhost:11434").

'http://localhost:11434'
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class OllamaNegotiator(LLMNegotiator):
    """LLM Negotiator using Ollama for local model inference.

    Args:
        model: Ollama model name (default: "llama3.2").
        api_base: Ollama server URL (default: "http://localhost:11434").
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "llama3.2",
        *,
        api_base: str = "http://localhost:11434",
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="ollama",
            model=model,
            api_base=api_base,
            **kwargs,
        )

Functions

__init__(model='llama3.2', *, api_base='http://localhost:11434', **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "llama3.2",
    *,
    api_base: str = "http://localhost:11434",
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="ollama",
        model=model,
        api_base=api_base,
        **kwargs,
    )

vLLM

negmas_llm.VLLMNegotiator

Bases: LLMNegotiator

LLM Negotiator using vLLM server for local model inference.

Parameters:

Name Type Description Default
model str

Model name as configured in vLLM.

required
api_base str

vLLM server URL (default: "http://localhost:8000/v1").

'http://localhost:8000/v1'
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class VLLMNegotiator(LLMNegotiator):
    """LLM Negotiator using vLLM server for local model inference.

    Args:
        model: Model name as configured in vLLM.
        api_base: vLLM server URL (default: "http://localhost:8000/v1").
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str,
        *,
        api_base: str = "http://localhost:8000/v1",
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="openai",  # vLLM exposes OpenAI-compatible API
            model=model,
            api_base=api_base,
            **kwargs,
        )

Functions

__init__(model, *, api_base='http://localhost:8000/v1', **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str,
    *,
    api_base: str = "http://localhost:8000/v1",
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="openai",  # vLLM exposes OpenAI-compatible API
        model=model,
        api_base=api_base,
        **kwargs,
    )

LM Studio

negmas_llm.LMStudioNegotiator

Bases: LLMNegotiator

LLM Negotiator using LM Studio for local model inference.

Parameters:

Name Type Description Default
model str

Model name (default: "local-model").

'local-model'
api_base str

LM Studio server URL (default: "http://localhost:1234/v1").

'http://localhost:1234/v1'
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class LMStudioNegotiator(LLMNegotiator):
    """LLM Negotiator using LM Studio for local model inference.

    Args:
        model: Model name (default: "local-model").
        api_base: LM Studio server URL (default: "http://localhost:1234/v1").
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "local-model",
        *,
        api_base: str = "http://localhost:1234/v1",
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="openai",  # LM Studio exposes OpenAI-compatible API
            model=model,
            api_base=api_base,
            **kwargs,
        )

Functions

__init__(model='local-model', *, api_base='http://localhost:1234/v1', **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "local-model",
    *,
    api_base: str = "http://localhost:1234/v1",
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="openai",  # LM Studio exposes OpenAI-compatible API
        model=model,
        api_base=api_base,
        **kwargs,
    )

Text Generation WebUI

negmas_llm.TextGenWebUINegotiator

Bases: LLMNegotiator

LLM Negotiator using text-generation-webui (oobabooga) server.

Parameters:

Name Type Description Default
model str

Model name.

'local-model'
api_base str

Server URL (default: "http://localhost:5000/v1").

'http://localhost:5000/v1'
**kwargs Any

Additional arguments passed to LLMNegotiator.

{}
Source code in src/negmas_llm/negotiator.py
class TextGenWebUINegotiator(LLMNegotiator):
    """LLM Negotiator using text-generation-webui (oobabooga) server.

    Args:
        model: Model name.
        api_base: Server URL (default: "http://localhost:5000/v1").
        **kwargs: Additional arguments passed to LLMNegotiator.
    """

    def __init__(
        self,
        model: str = "local-model",
        *,
        api_base: str = "http://localhost:5000/v1",
        **kwargs: Any,
    ) -> None:
        super().__init__(
            provider="openai",  # oobabooga exposes OpenAI-compatible API
            model=model,
            api_base=api_base,
            **kwargs,
        )

Functions

__init__(model='local-model', *, api_base='http://localhost:5000/v1', **kwargs)

Source code in src/negmas_llm/negotiator.py
def __init__(
    self,
    model: str = "local-model",
    *,
    api_base: str = "http://localhost:5000/v1",
    **kwargs: Any,
) -> None:
    super().__init__(
        provider="openai",  # oobabooga exposes OpenAI-compatible API
        model=model,
        api_base=api_base,
        **kwargs,
    )