letta-nightly 0.5.2.dev20241118104226__py3-none-any.whl → 0.5.3.dev20241120010849__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of letta-nightly might be problematic. Click here for more details.

letta/metadata.py CHANGED
@@ -4,23 +4,13 @@ import os
4
4
  import secrets
5
5
  from typing import List, Optional
6
6
 
7
- from sqlalchemy import (
8
- BIGINT,
9
- JSON,
10
- Boolean,
11
- Column,
12
- DateTime,
13
- Index,
14
- String,
15
- TypeDecorator,
16
- )
7
+ from sqlalchemy import JSON, Column, DateTime, Index, String, TypeDecorator
17
8
  from sqlalchemy.sql import func
18
9
 
19
10
  from letta.config import LettaConfig
20
11
  from letta.orm.base import Base
21
12
  from letta.schemas.agent import AgentState
22
13
  from letta.schemas.api_key import APIKey
23
- from letta.schemas.block import Block, Human, Persona
24
14
  from letta.schemas.embedding_config import EmbeddingConfig
25
15
  from letta.schemas.enums import JobStatus
26
16
  from letta.schemas.job import Job
@@ -269,63 +259,6 @@ class AgentSourceMappingModel(Base):
269
259
  return f"<AgentSourceMapping(user_id='{self.user_id}', agent_id='{self.agent_id}', source_id='{self.source_id}')>"
270
260
 
271
261
 
272
- class BlockModel(Base):
273
- __tablename__ = "block"
274
- __table_args__ = {"extend_existing": True}
275
-
276
- id = Column(String, primary_key=True, nullable=False)
277
- value = Column(String, nullable=False)
278
- limit = Column(BIGINT)
279
- template_name = Column(String, nullable=True, default=None)
280
- template = Column(Boolean, default=False) # True: listed as possible human/persona
281
- label = Column(String, nullable=False)
282
- metadata_ = Column(JSON)
283
- description = Column(String)
284
- user_id = Column(String)
285
- Index(__tablename__ + "_idx_user", user_id),
286
-
287
- def __repr__(self) -> str:
288
- return f"<Block(id='{self.id}', template_name='{self.template_name}', template='{self.template_name}', label='{self.label}', user_id='{self.user_id}')>"
289
-
290
- def to_record(self) -> Block:
291
- if self.label == "persona":
292
- return Persona(
293
- id=self.id,
294
- value=self.value,
295
- limit=self.limit,
296
- template_name=self.template_name,
297
- template=self.template,
298
- label=self.label,
299
- metadata_=self.metadata_,
300
- description=self.description,
301
- user_id=self.user_id,
302
- )
303
- elif self.label == "human":
304
- return Human(
305
- id=self.id,
306
- value=self.value,
307
- limit=self.limit,
308
- template_name=self.template_name,
309
- template=self.template,
310
- label=self.label,
311
- metadata_=self.metadata_,
312
- description=self.description,
313
- user_id=self.user_id,
314
- )
315
- else:
316
- return Block(
317
- id=self.id,
318
- value=self.value,
319
- limit=self.limit,
320
- template_name=self.template_name,
321
- template=self.template,
322
- label=self.label,
323
- metadata_=self.metadata_,
324
- description=self.description,
325
- user_id=self.user_id,
326
- )
327
-
328
-
329
262
  class JobModel(Base):
330
263
  __tablename__ = "jobs"
331
264
  __table_args__ = {"extend_existing": True}
@@ -425,27 +358,6 @@ class MetadataStore:
425
358
  session.add(AgentModel(**fields))
426
359
  session.commit()
427
360
 
428
- @enforce_types
429
- def create_block(self, block: Block):
430
- with self.session_maker() as session:
431
- # TODO: fix?
432
- # we are only validating that more than one template block
433
- # with a given name doesn't exist.
434
- if (
435
- session.query(BlockModel)
436
- .filter(BlockModel.template_name == block.template_name)
437
- .filter(BlockModel.user_id == block.user_id)
438
- .filter(BlockModel.template == True)
439
- .filter(BlockModel.label == block.label)
440
- .count()
441
- > 0
442
- ):
443
-
444
- raise ValueError(f"Block with name {block.template_name} already exists")
445
-
446
- session.add(BlockModel(**vars(block)))
447
- session.commit()
448
-
449
361
  @enforce_types
450
362
  def update_agent(self, agent: AgentState):
451
363
  with self.session_maker() as session:
@@ -457,28 +369,6 @@ class MetadataStore:
457
369
  session.query(AgentModel).filter(AgentModel.id == agent.id).update(fields)
458
370
  session.commit()
459
371
 
460
- @enforce_types
461
- def update_block(self, block: Block):
462
- with self.session_maker() as session:
463
- session.query(BlockModel).filter(BlockModel.id == block.id).update(vars(block))
464
- session.commit()
465
-
466
- @enforce_types
467
- def update_or_create_block(self, block: Block):
468
- with self.session_maker() as session:
469
- existing_block = session.query(BlockModel).filter(BlockModel.id == block.id).first()
470
- if existing_block:
471
- session.query(BlockModel).filter(BlockModel.id == block.id).update(vars(block))
472
- else:
473
- session.add(BlockModel(**vars(block)))
474
- session.commit()
475
-
476
- @enforce_types
477
- def delete_block(self, block_id: str):
478
- with self.session_maker() as session:
479
- session.query(BlockModel).filter(BlockModel.id == block_id).delete()
480
- session.commit()
481
-
482
372
  @enforce_types
483
373
  def delete_agent(self, agent_id: str):
484
374
  with self.session_maker() as session:
@@ -513,50 +403,6 @@ class MetadataStore:
513
403
  assert len(results) == 1, f"Expected 1 result, got {len(results)}" # should only be one result
514
404
  return results[0].to_record()
515
405
 
516
- @enforce_types
517
- def get_block(self, block_id: str) -> Optional[Block]:
518
- with self.session_maker() as session:
519
- results = session.query(BlockModel).filter(BlockModel.id == block_id).all()
520
- if len(results) == 0:
521
- return None
522
- assert len(results) == 1, f"Expected 1 result, got {len(results)}"
523
- return results[0].to_record()
524
-
525
- @enforce_types
526
- def get_blocks(
527
- self,
528
- user_id: Optional[str],
529
- label: Optional[str] = None,
530
- template: Optional[bool] = None,
531
- template_name: Optional[str] = None,
532
- id: Optional[str] = None,
533
- ) -> Optional[List[Block]]:
534
- """List available blocks"""
535
- with self.session_maker() as session:
536
- query = session.query(BlockModel)
537
-
538
- if user_id:
539
- query = query.filter(BlockModel.user_id == user_id)
540
-
541
- if label:
542
- query = query.filter(BlockModel.label == label)
543
-
544
- if template_name:
545
- query = query.filter(BlockModel.template_name == template_name)
546
-
547
- if id:
548
- query = query.filter(BlockModel.id == id)
549
-
550
- if template:
551
- query = query.filter(BlockModel.template == template)
552
-
553
- results = query.all()
554
-
555
- if len(results) == 0:
556
- return None
557
-
558
- return [r.to_record() for r in results]
559
-
560
406
  # agent source metadata
561
407
  @enforce_types
562
408
  def attach_source(self, user_id: str, agent_id: str, source_id: str):
letta/o1_agent.py CHANGED
@@ -8,6 +8,7 @@ from letta.schemas.message import Message
8
8
  from letta.schemas.openai.chat_completion_response import UsageStatistics
9
9
  from letta.schemas.tool import Tool
10
10
  from letta.schemas.usage import LettaUsageStatistics
11
+ from letta.schemas.user import User
11
12
 
12
13
 
13
14
  def send_thinking_message(self: "Agent", message: str) -> Optional[str]:
@@ -43,11 +44,12 @@ class O1Agent(Agent):
43
44
  self,
44
45
  interface: AgentInterface,
45
46
  agent_state: AgentState,
47
+ user: User,
46
48
  tools: List[Tool] = [],
47
49
  max_thinking_steps: int = 10,
48
50
  first_message_verify_mono: bool = False,
49
51
  ):
50
- super().__init__(interface, agent_state, tools)
52
+ super().__init__(interface, agent_state, tools, user)
51
53
  self.max_thinking_steps = max_thinking_steps
52
54
  self.tools = tools
53
55
  self.first_message_verify_mono = first_message_verify_mono
letta/orm/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from letta.orm.base import Base
2
+ from letta.orm.block import Block
2
3
  from letta.orm.file import FileMetadata
3
4
  from letta.orm.organization import Organization
4
5
  from letta.orm.source import Source
letta/orm/block.py ADDED
@@ -0,0 +1,44 @@
1
+ from typing import TYPE_CHECKING, Optional, Type
2
+
3
+ from sqlalchemy import JSON, BigInteger, Integer
4
+ from sqlalchemy.orm import Mapped, mapped_column, relationship
5
+
6
+ from letta.orm.mixins import OrganizationMixin
7
+ from letta.orm.sqlalchemy_base import SqlalchemyBase
8
+ from letta.schemas.block import Block as PydanticBlock
9
+ from letta.schemas.block import Human, Persona
10
+
11
+ if TYPE_CHECKING:
12
+ from letta.orm.organization import Organization
13
+
14
+
15
+ class Block(OrganizationMixin, SqlalchemyBase):
16
+ """Blocks are sections of the LLM context, representing a specific part of the total Memory"""
17
+
18
+ __tablename__ = "block"
19
+ __pydantic_model__ = PydanticBlock
20
+
21
+ template_name: Mapped[Optional[str]] = mapped_column(
22
+ nullable=True, doc="the unique name that identifies a block in a human-readable way"
23
+ )
24
+ description: Mapped[Optional[str]] = mapped_column(nullable=True, doc="a description of the block for context")
25
+ label: Mapped[str] = mapped_column(doc="the type of memory block in use, ie 'human', 'persona', 'system'")
26
+ is_template: Mapped[bool] = mapped_column(
27
+ doc="whether the block is a template (e.g. saved human/persona options as baselines for other templates)", default=False
28
+ )
29
+ value: Mapped[str] = mapped_column(doc="Text content of the block for the respective section of core memory.")
30
+ limit: Mapped[BigInteger] = mapped_column(Integer, default=2000, doc="Character limit of the block.")
31
+ metadata_: Mapped[Optional[dict]] = mapped_column(JSON, default={}, doc="arbitrary information related to the block.")
32
+
33
+ # relationships
34
+ organization: Mapped[Optional["Organization"]] = relationship("Organization")
35
+
36
+ def to_pydantic(self) -> Type:
37
+ match self.label:
38
+ case "human":
39
+ Schema = Human
40
+ case "persona":
41
+ Schema = Persona
42
+ case _:
43
+ Schema = PydanticBlock
44
+ return Schema.model_validate(self)
letta/orm/organization.py CHANGED
@@ -23,6 +23,7 @@ class Organization(SqlalchemyBase):
23
23
  # relationships
24
24
  users: Mapped[List["User"]] = relationship("User", back_populates="organization", cascade="all, delete-orphan")
25
25
  tools: Mapped[List["Tool"]] = relationship("Tool", back_populates="organization", cascade="all, delete-orphan")
26
+ blocks: Mapped[List["Block"]] = relationship("Block", back_populates="organization", cascade="all, delete-orphan")
26
27
  sources: Mapped[List["Source"]] = relationship("Source", back_populates="organization", cascade="all, delete-orphan")
27
28
  agents_tags: Mapped[List["AgentsTags"]] = relationship("AgentsTags", back_populates="organization", cascade="all, delete-orphan")
28
29
  files: Mapped[List["FileMetadata"]] = relationship("FileMetadata", back_populates="organization", cascade="all, delete-orphan")
letta/providers.py CHANGED
@@ -2,7 +2,7 @@ from typing import List, Optional
2
2
 
3
3
  from pydantic import BaseModel, Field, model_validator
4
4
 
5
- from letta.constants import LLM_MAX_TOKENS
5
+ from letta.constants import LLM_MAX_TOKENS, MIN_CONTEXT_WINDOW
6
6
  from letta.llm_api.azure_openai import (
7
7
  get_azure_chat_completions_endpoint,
8
8
  get_azure_embeddings_endpoint,
@@ -67,10 +67,15 @@ class OpenAIProvider(Provider):
67
67
  extra_params = {"supported_parameters": "tools"} if "openrouter.ai" in self.base_url else None
68
68
  response = openai_get_model_list(self.base_url, api_key=self.api_key, extra_params=extra_params)
69
69
 
70
- assert "data" in response, f"OpenAI model query response missing 'data' field: {response}"
70
+ # TogetherAI's response is missing the 'data' field
71
+ # assert "data" in response, f"OpenAI model query response missing 'data' field: {response}"
72
+ if "data" in response:
73
+ data = response["data"]
74
+ else:
75
+ data = response
71
76
 
72
77
  configs = []
73
- for model in response["data"]:
78
+ for model in data:
74
79
  assert "id" in model, f"OpenAI model missing 'id' field: {model}"
75
80
  model_name = model["id"]
76
81
 
@@ -82,6 +87,32 @@ class OpenAIProvider(Provider):
82
87
 
83
88
  if not context_window_size:
84
89
  continue
90
+
91
+ # TogetherAI includes the type, which we can use to filter out embedding models
92
+ if self.base_url == "https://api.together.ai/v1":
93
+ if "type" in model and model["type"] != "chat":
94
+ continue
95
+
96
+ # for TogetherAI, we need to skip the models that don't support JSON mode / function calling
97
+ # requests.exceptions.HTTPError: HTTP error occurred: 400 Client Error: Bad Request for url: https://api.together.ai/v1/chat/completions | Status code: 400, Message: {
98
+ # "error": {
99
+ # "message": "mistralai/Mixtral-8x7B-v0.1 is not supported for JSON mode/function calling",
100
+ # "type": "invalid_request_error",
101
+ # "param": null,
102
+ # "code": "constraints_model"
103
+ # }
104
+ # }
105
+ if "config" not in model:
106
+ continue
107
+ if "chat_template" not in model["config"]:
108
+ continue
109
+ if model["config"]["chat_template"] is None:
110
+ continue
111
+ if "tools" not in model["config"]["chat_template"]:
112
+ continue
113
+ # if "config" in data and "chat_template" in data["config"] and "tools" not in data["config"]["chat_template"]:
114
+ # continue
115
+
85
116
  configs.append(
86
117
  LLMConfig(model=model_name, model_endpoint_type="openai", model_endpoint=self.base_url, context_window=context_window_size)
87
118
  )
@@ -325,6 +356,113 @@ class GroqProvider(OpenAIProvider):
325
356
  raise NotImplementedError
326
357
 
327
358
 
359
+ class TogetherProvider(OpenAIProvider):
360
+ """TogetherAI provider that uses the /completions API
361
+
362
+ TogetherAI can also be used via the /chat/completions API
363
+ by settings OPENAI_API_KEY and OPENAI_API_BASE to the TogetherAI API key
364
+ and API URL, however /completions is preferred because their /chat/completions
365
+ function calling support is limited.
366
+ """
367
+
368
+ name: str = "together"
369
+ base_url: str = "https://api.together.ai/v1"
370
+ api_key: str = Field(..., description="API key for the TogetherAI API.")
371
+ default_prompt_formatter: str = Field(..., description="Default prompt formatter (aka model wrapper) to use on vLLM /completions API.")
372
+
373
+ def list_llm_models(self) -> List[LLMConfig]:
374
+ from letta.llm_api.openai import openai_get_model_list
375
+
376
+ response = openai_get_model_list(self.base_url, api_key=self.api_key)
377
+
378
+ # TogetherAI's response is missing the 'data' field
379
+ # assert "data" in response, f"OpenAI model query response missing 'data' field: {response}"
380
+ if "data" in response:
381
+ data = response["data"]
382
+ else:
383
+ data = response
384
+
385
+ configs = []
386
+ for model in data:
387
+ assert "id" in model, f"TogetherAI model missing 'id' field: {model}"
388
+ model_name = model["id"]
389
+
390
+ if "context_length" in model:
391
+ # Context length is returned in OpenRouter as "context_length"
392
+ context_window_size = model["context_length"]
393
+ else:
394
+ context_window_size = self.get_model_context_window_size(model_name)
395
+
396
+ # We need the context length for embeddings too
397
+ if not context_window_size:
398
+ continue
399
+
400
+ # Skip models that are too small for Letta
401
+ if context_window_size <= MIN_CONTEXT_WINDOW:
402
+ continue
403
+
404
+ # TogetherAI includes the type, which we can use to filter for embedding models
405
+ if "type" in model and model["type"] not in ["chat", "language"]:
406
+ continue
407
+
408
+ configs.append(
409
+ LLMConfig(
410
+ model=model_name,
411
+ model_endpoint_type="together",
412
+ model_endpoint=self.base_url,
413
+ model_wrapper=self.default_prompt_formatter,
414
+ context_window=context_window_size,
415
+ )
416
+ )
417
+
418
+ return configs
419
+
420
+ def list_embedding_models(self) -> List[EmbeddingConfig]:
421
+ # TODO renable once we figure out how to pass API keys through properly
422
+ return []
423
+
424
+ # from letta.llm_api.openai import openai_get_model_list
425
+
426
+ # response = openai_get_model_list(self.base_url, api_key=self.api_key)
427
+
428
+ # # TogetherAI's response is missing the 'data' field
429
+ # # assert "data" in response, f"OpenAI model query response missing 'data' field: {response}"
430
+ # if "data" in response:
431
+ # data = response["data"]
432
+ # else:
433
+ # data = response
434
+
435
+ # configs = []
436
+ # for model in data:
437
+ # assert "id" in model, f"TogetherAI model missing 'id' field: {model}"
438
+ # model_name = model["id"]
439
+
440
+ # if "context_length" in model:
441
+ # # Context length is returned in OpenRouter as "context_length"
442
+ # context_window_size = model["context_length"]
443
+ # else:
444
+ # context_window_size = self.get_model_context_window_size(model_name)
445
+
446
+ # if not context_window_size:
447
+ # continue
448
+
449
+ # # TogetherAI includes the type, which we can use to filter out embedding models
450
+ # if "type" in model and model["type"] not in ["embedding"]:
451
+ # continue
452
+
453
+ # configs.append(
454
+ # EmbeddingConfig(
455
+ # embedding_model=model_name,
456
+ # embedding_endpoint_type="openai",
457
+ # embedding_endpoint=self.base_url,
458
+ # embedding_dim=context_window_size,
459
+ # embedding_chunk_size=300, # TODO: change?
460
+ # )
461
+ # )
462
+
463
+ # return configs
464
+
465
+
328
466
  class GoogleAIProvider(Provider):
329
467
  # gemini
330
468
  api_key: str = Field(..., description="API key for the Google AI API.")
letta/schemas/block.py CHANGED
@@ -14,36 +14,30 @@ class BaseBlock(LettaBase, validate_assignment=True):
14
14
  __id_prefix__ = "block"
15
15
 
16
16
  # data value
17
- value: Optional[str] = Field(None, description="Value of the block.")
17
+ value: str = Field(..., description="Value of the block.")
18
18
  limit: int = Field(2000, description="Character limit of the block.")
19
19
 
20
20
  # template data (optional)
21
21
  template_name: Optional[str] = Field(None, description="Name of the block if it is a template.", alias="name")
22
- template: bool = Field(False, description="Whether the block is a template (e.g. saved human/persona options).")
22
+ is_template: bool = Field(False, description="Whether the block is a template (e.g. saved human/persona options).")
23
23
 
24
24
  # context window label
25
- label: str = Field(None, description="Label of the block (e.g. 'human', 'persona') in the context window.")
25
+ label: Optional[str] = Field(None, description="Label of the block (e.g. 'human', 'persona') in the context window.")
26
26
 
27
27
  # metadata
28
28
  description: Optional[str] = Field(None, description="Description of the block.")
29
29
  metadata_: Optional[dict] = Field({}, description="Metadata of the block.")
30
30
 
31
- # associated user/agent
32
- user_id: Optional[str] = Field(None, description="The unique identifier of the user associated with the block.")
33
-
34
31
  @model_validator(mode="after")
35
32
  def verify_char_limit(self) -> Self:
36
- try:
37
- assert len(self) <= self.limit
38
- except AssertionError:
39
- error_msg = f"Edit failed: Exceeds {self.limit} character limit (requested {len(self)}) - {str(self)}."
33
+ if len(self.value) > self.limit:
34
+ error_msg = f"Edit failed: Exceeds {self.limit} character limit (requested {len(self.value)}) - {str(self)}."
40
35
  raise ValueError(error_msg)
41
- except Exception as e:
42
- raise e
36
+
43
37
  return self
44
38
 
45
- def __len__(self):
46
- return len(self.value)
39
+ # def __len__(self):
40
+ # return len(self.value)
47
41
 
48
42
  def __setattr__(self, name, value):
49
43
  """Run validation if self.value is updated"""
@@ -52,6 +46,9 @@ class BaseBlock(LettaBase, validate_assignment=True):
52
46
  # run validation
53
47
  self.__class__.model_validate(self.model_dump(exclude_unset=True))
54
48
 
49
+ class Config:
50
+ extra = "ignore" # Ignores extra fields
51
+
55
52
 
56
53
  class Block(BaseBlock):
57
54
  """
@@ -61,15 +58,22 @@ class Block(BaseBlock):
61
58
  label (str): The label of the block (e.g. 'human', 'persona'). This defines a category for the block.
62
59
  value (str): The value of the block. This is the string that is represented in the context window.
63
60
  limit (int): The character limit of the block.
61
+ is_template (bool): Whether the block is a template (e.g. saved human/persona options). Non-template blocks are not stored in the database and are ephemeral, while templated blocks are stored in the database.
62
+ label (str): The label of the block (e.g. 'human', 'persona'). This defines a category for the block.
64
63
  template_name (str): The name of the block template (if it is a template).
65
- template (bool): Whether the block is a template (e.g. saved human/persona options). Non-template blocks are not stored in the database and are ephemeral, while templated blocks are stored in the database.
66
64
  description (str): Description of the block.
67
65
  metadata_ (Dict): Metadata of the block.
68
66
  user_id (str): The unique identifier of the user associated with the block.
69
67
  """
70
68
 
71
69
  id: str = BaseBlock.generate_id_field()
72
- value: str = Field(..., description="Value of the block.")
70
+
71
+ # associated user/agent
72
+ organization_id: Optional[str] = Field(None, description="The unique identifier of the organization associated with the block.")
73
+
74
+ # default orm fields
75
+ created_by_id: Optional[str] = Field(None, description="The id of the user that made this Block.")
76
+ last_updated_by_id: Optional[str] = Field(None, description="The id of the user that last updated this Block.")
73
77
 
74
78
 
75
79
  class Human(Block):
@@ -84,41 +88,42 @@ class Persona(Block):
84
88
  label: str = "persona"
85
89
 
86
90
 
87
- class CreateBlock(BaseBlock):
91
+ class BlockCreate(BaseBlock):
88
92
  """Create a block"""
89
93
 
90
- template: bool = True
94
+ is_template: bool = True
91
95
  label: str = Field(..., description="Label of the block.")
92
96
 
93
97
 
94
- class CreatePersona(BaseBlock):
98
+ class CreatePersona(BlockCreate):
95
99
  """Create a persona block"""
96
100
 
97
- template: bool = True
98
101
  label: str = "persona"
99
102
 
100
103
 
101
- class CreateHuman(BaseBlock):
104
+ class CreateHuman(BlockCreate):
102
105
  """Create a human block"""
103
106
 
104
- template: bool = True
105
107
  label: str = "human"
106
108
 
107
109
 
108
- class UpdateBlock(BaseBlock):
110
+ class BlockUpdate(BaseBlock):
109
111
  """Update a block"""
110
112
 
111
- id: str = Field(..., description="The unique identifier of the block.")
112
113
  limit: Optional[int] = Field(2000, description="Character limit of the block.")
114
+ value: Optional[str] = Field(None, description="Value of the block.")
115
+
116
+ class Config:
117
+ extra = "ignore" # Ignores extra fields
113
118
 
114
119
 
115
- class UpdatePersona(UpdateBlock):
120
+ class UpdatePersona(BlockUpdate):
116
121
  """Update a persona block"""
117
122
 
118
123
  label: str = "persona"
119
124
 
120
125
 
121
- class UpdateHuman(UpdateBlock):
126
+ class UpdateHuman(BlockUpdate):
122
127
  """Update a human block"""
123
128
 
124
129
  label: str = "human"
@@ -77,6 +77,6 @@ class LettaBase(BaseModel):
77
77
  """
78
78
  _ = values # for SCA
79
79
  if isinstance(v, UUID):
80
- logger.warning("Bare UUIDs are deprecated, please use the full prefixed id!")
80
+ logger.warning(f"Bare UUIDs are deprecated, please use the full prefixed id ({cls.__id_prefix__})!")
81
81
  return f"{cls.__id_prefix__}-{v}"
82
82
  return v
@@ -35,6 +35,7 @@ class LLMConfig(BaseModel):
35
35
  "vllm",
36
36
  "hugging-face",
37
37
  "mistral",
38
+ "together", # completions endpoint
38
39
  ] = Field(..., description="The endpoint type for the model.")
39
40
  model_endpoint: Optional[str] = Field(None, description="The endpoint for the model.")
40
41
  model_wrapper: Optional[str] = Field(None, description="The wrapper for the model.")
@@ -46,6 +46,7 @@ class Choice(BaseModel):
46
46
  index: int
47
47
  message: Message
48
48
  logprobs: Optional[Dict[str, Union[List[MessageContentLogProb], None]]] = None
49
+ seed: Optional[int] = None # found in TogetherAI
49
50
 
50
51
 
51
52
  class UsageStatistics(BaseModel):