remdb 0.2.6__py3-none-any.whl → 0.3.103__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 remdb might be problematic. Click here for more details.

Files changed (82) hide show
  1. rem/__init__.py +129 -2
  2. rem/agentic/README.md +76 -0
  3. rem/agentic/__init__.py +15 -0
  4. rem/agentic/agents/__init__.py +16 -2
  5. rem/agentic/agents/sse_simulator.py +500 -0
  6. rem/agentic/context.py +7 -5
  7. rem/agentic/llm_provider_models.py +301 -0
  8. rem/agentic/providers/phoenix.py +32 -43
  9. rem/agentic/providers/pydantic_ai.py +84 -10
  10. rem/api/README.md +238 -1
  11. rem/api/deps.py +255 -0
  12. rem/api/main.py +70 -22
  13. rem/api/mcp_router/server.py +8 -1
  14. rem/api/mcp_router/tools.py +80 -0
  15. rem/api/middleware/tracking.py +172 -0
  16. rem/api/routers/admin.py +277 -0
  17. rem/api/routers/auth.py +124 -0
  18. rem/api/routers/chat/completions.py +123 -14
  19. rem/api/routers/chat/models.py +7 -3
  20. rem/api/routers/chat/sse_events.py +526 -0
  21. rem/api/routers/chat/streaming.py +468 -45
  22. rem/api/routers/dev.py +81 -0
  23. rem/api/routers/feedback.py +455 -0
  24. rem/api/routers/messages.py +473 -0
  25. rem/api/routers/models.py +78 -0
  26. rem/api/routers/shared_sessions.py +406 -0
  27. rem/auth/middleware.py +126 -27
  28. rem/cli/commands/ask.py +15 -11
  29. rem/cli/commands/configure.py +169 -94
  30. rem/cli/commands/db.py +53 -7
  31. rem/cli/commands/experiments.py +278 -96
  32. rem/cli/commands/process.py +8 -7
  33. rem/cli/commands/scaffold.py +47 -0
  34. rem/cli/commands/schema.py +9 -9
  35. rem/cli/main.py +10 -0
  36. rem/config.py +2 -2
  37. rem/models/core/core_model.py +7 -1
  38. rem/models/entities/__init__.py +21 -0
  39. rem/models/entities/domain_resource.py +38 -0
  40. rem/models/entities/feedback.py +123 -0
  41. rem/models/entities/message.py +30 -1
  42. rem/models/entities/session.py +83 -0
  43. rem/models/entities/shared_session.py +206 -0
  44. rem/models/entities/user.py +10 -3
  45. rem/registry.py +367 -0
  46. rem/schemas/agents/rem.yaml +7 -3
  47. rem/services/content/providers.py +94 -140
  48. rem/services/content/service.py +85 -16
  49. rem/services/dreaming/affinity_service.py +2 -16
  50. rem/services/dreaming/moment_service.py +2 -15
  51. rem/services/embeddings/api.py +20 -13
  52. rem/services/phoenix/EXPERIMENT_DESIGN.md +3 -3
  53. rem/services/phoenix/client.py +252 -19
  54. rem/services/postgres/README.md +29 -10
  55. rem/services/postgres/repository.py +132 -0
  56. rem/services/postgres/schema_generator.py +86 -5
  57. rem/services/rate_limit.py +113 -0
  58. rem/services/rem/README.md +14 -0
  59. rem/services/session/compression.py +17 -1
  60. rem/services/user_service.py +98 -0
  61. rem/settings.py +115 -17
  62. rem/sql/background_indexes.sql +10 -0
  63. rem/sql/migrations/001_install.sql +152 -2
  64. rem/sql/migrations/002_install_models.sql +580 -231
  65. rem/sql/migrations/003_seed_default_user.sql +48 -0
  66. rem/utils/constants.py +97 -0
  67. rem/utils/date_utils.py +228 -0
  68. rem/utils/embeddings.py +17 -4
  69. rem/utils/files.py +167 -0
  70. rem/utils/mime_types.py +158 -0
  71. rem/utils/model_helpers.py +156 -1
  72. rem/utils/schema_loader.py +273 -14
  73. rem/utils/sql_types.py +3 -1
  74. rem/utils/vision.py +9 -14
  75. rem/workers/README.md +14 -14
  76. rem/workers/db_maintainer.py +74 -0
  77. {remdb-0.2.6.dist-info → remdb-0.3.103.dist-info}/METADATA +486 -132
  78. {remdb-0.2.6.dist-info → remdb-0.3.103.dist-info}/RECORD +80 -57
  79. {remdb-0.2.6.dist-info → remdb-0.3.103.dist-info}/WHEEL +1 -1
  80. rem/sql/002_install_models.sql +0 -1068
  81. rem/sql/install_models.sql +0 -1038
  82. {remdb-0.2.6.dist-info → remdb-0.3.103.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,47 @@
1
+ """
2
+ Scaffold command - generate project structure for REM-based applications.
3
+
4
+ TODO: Implement this command to generate:
5
+ - my_app/main.py (entry point with create_app)
6
+ - my_app/models.py (example CoreModel subclass)
7
+ - my_app/routers/ (example FastAPI router)
8
+ - schemas/agents/ (example agent schema)
9
+ - schemas/evaluators/ (example evaluator)
10
+ - sql/migrations/ (empty migrations directory)
11
+ - pyproject.toml (with remdb dependency)
12
+ - README.md (basic usage instructions)
13
+
14
+ Usage:
15
+ rem scaffold my-app
16
+ rem scaffold my-app --with-examples # Include example models/routers/tools
17
+ """
18
+
19
+ import click
20
+
21
+
22
+ @click.command()
23
+ @click.argument("name")
24
+ @click.option("--with-examples", is_flag=True, help="Include example code")
25
+ def scaffold(name: str, with_examples: bool) -> None:
26
+ """
27
+ Generate a new REM-based project structure.
28
+
29
+ NAME is the project directory name to create.
30
+ """
31
+ click.echo(f"TODO: Scaffold command not yet implemented")
32
+ click.echo(f"Would create project: {name}")
33
+ click.echo(f"With examples: {with_examples}")
34
+ click.echo()
35
+ click.echo("For now, manually create this structure:")
36
+ click.echo(f"""
37
+ {name}/
38
+ ├── {name.replace('-', '_')}/
39
+ │ ├── main.py # Entry point (create_app + extensions)
40
+ │ ├── models.py # Custom models (inherit CoreModel)
41
+ │ └── routers/ # Custom FastAPI routers
42
+ ├── schemas/
43
+ │ ├── agents/ # Custom agent YAML schemas
44
+ │ └── evaluators/ # Custom evaluator schemas
45
+ ├── sql/migrations/ # Custom SQL migrations
46
+ └── pyproject.toml
47
+ """)
@@ -29,14 +29,14 @@ from ...services.postgres.schema_generator import SchemaGenerator
29
29
  "--output",
30
30
  "-o",
31
31
  type=click.Path(path_type=Path),
32
- default="install_models.sql",
33
- help="Output SQL file (default: install_models.sql)",
32
+ default="002_install_models.sql",
33
+ help="Output SQL file (default: 002_install_models.sql)",
34
34
  )
35
35
  @click.option(
36
36
  "--output-dir",
37
37
  type=click.Path(path_type=Path),
38
38
  default=None,
39
- help=f"Base output directory (default: {settings.sql_dir})",
39
+ help=f"Base output directory (default: {settings.sql_dir}/migrations)",
40
40
  )
41
41
  def generate(models: Path, output: Path, output_dir: Path | None):
42
42
  """
@@ -48,13 +48,13 @@ def generate(models: Path, output: Path, output_dir: Path | None):
48
48
  - KV_STORE triggers for cache maintenance
49
49
  - Indexes (foreground only)
50
50
 
51
- Output is written to src/rem/sql/install_models.sql by default.
51
+ Output is written to src/rem/sql/migrations/002_install_models.sql by default.
52
52
 
53
53
  Example:
54
54
  rem db schema generate --models src/rem/models/entities
55
55
 
56
56
  This creates:
57
- - src/rem/sql/install_models.sql - Entity tables and triggers
57
+ - src/rem/sql/migrations/002_install_models.sql - Entity tables and triggers
58
58
  - src/rem/sql/background_indexes.sql - HNSW indexes (apply after data load)
59
59
 
60
60
  After generation, apply with:
@@ -62,8 +62,8 @@ def generate(models: Path, output: Path, output_dir: Path | None):
62
62
  """
63
63
  click.echo(f"Discovering models in {models}")
64
64
 
65
- # Use settings.sql_dir if not provided
66
- actual_output_dir = output_dir or Path(settings.sql_dir)
65
+ # Default to migrations directory
66
+ actual_output_dir = output_dir or Path(settings.sql_dir) / "migrations"
67
67
  generator = SchemaGenerator(output_dir=actual_output_dir)
68
68
 
69
69
  # Generate schema
@@ -73,10 +73,10 @@ def generate(models: Path, output: Path, output_dir: Path | None):
73
73
  click.echo(f"✓ Schema generated: {len(generator.schemas)} tables")
74
74
  click.echo(f"✓ Written to: {actual_output_dir / output.name}")
75
75
 
76
- # Generate background indexes
76
+ # Generate background indexes in parent sql dir
77
77
  background_indexes = generator.generate_background_indexes()
78
78
  if background_indexes:
79
- bg_file = actual_output_dir / "background_indexes.sql"
79
+ bg_file = Path(settings.sql_dir) / "background_indexes.sql"
80
80
  bg_file.write_text(background_indexes)
81
81
  click.echo(f"✓ Background indexes: {bg_file}")
82
82
 
rem/cli/main.py CHANGED
@@ -14,9 +14,17 @@ from pathlib import Path
14
14
  import click
15
15
  from loguru import logger
16
16
 
17
+ # Import version from package
18
+ try:
19
+ from importlib.metadata import version
20
+ __version__ = version("remdb")
21
+ except Exception:
22
+ __version__ = "unknown"
23
+
17
24
 
18
25
  @click.group()
19
26
  @click.option("--verbose", "-v", is_flag=True, help="Enable verbose logging")
27
+ @click.version_option(version=__version__, prog_name="rem")
20
28
  def cli(verbose: bool):
21
29
  """REM - Resources Entities Moments system CLI."""
22
30
  if verbose:
@@ -67,6 +75,7 @@ from .commands.experiments import experiments as experiments_group
67
75
  from .commands.configure import register_command as register_configure_command
68
76
  from .commands.serve import register_command as register_serve_command
69
77
  from .commands.mcp import register_command as register_mcp_command
78
+ from .commands.scaffold import scaffold as scaffold_command
70
79
 
71
80
  register_schema_commands(schema)
72
81
  register_db_commands(db)
@@ -77,6 +86,7 @@ register_configure_command(cli)
77
86
  register_serve_command(cli)
78
87
  register_mcp_command(cli)
79
88
  cli.add_command(experiments_group)
89
+ cli.add_command(scaffold_command)
80
90
 
81
91
 
82
92
  def main():
rem/config.py CHANGED
@@ -15,7 +15,7 @@ File Format (~/.rem/config.yaml):
15
15
  pool_max_size: 20
16
16
 
17
17
  llm:
18
- default_model: anthropic:claude-sonnet-4-5-20250929
18
+ default_model: openai:gpt-4.1
19
19
  openai_api_key: sk-...
20
20
  anthropic_api_key: sk-ant-...
21
21
 
@@ -216,7 +216,7 @@ def get_default_config() -> dict[str, Any]:
216
216
  "pool_max_size": 20,
217
217
  },
218
218
  "llm": {
219
- "default_model": "anthropic:claude-sonnet-4-5-20250929",
219
+ "default_model": "openai:gpt-4.1",
220
220
  "default_temperature": 0.5,
221
221
  # API keys will be prompted for in wizard
222
222
  # "openai_api_key": "",
@@ -52,7 +52,13 @@ class CoreModel(BaseModel):
52
52
  default=None, description="Tenant identifier for multi-tenancy isolation"
53
53
  )
54
54
  user_id: Optional[str] = Field(
55
- default=None, description="Owner user identifier (tenant-scoped)"
55
+ default=None,
56
+ description=(
57
+ "Owner user identifier (tenant-scoped). This is a VARCHAR(256), not a UUID, "
58
+ "to allow flexibility for external identity providers. Typically generated as "
59
+ "a hash of the user's email address. In future, other strong unique claims "
60
+ "(e.g., OAuth sub, verified phone) could also be used for generation."
61
+ ),
56
62
  )
57
63
  graph_edges: list[dict] = Field(
58
64
  default_factory=list,
@@ -5,6 +5,9 @@ Core entity types for the REM system:
5
5
  - Resources: Base content units (documents, conversations, artifacts)
6
6
  - ImageResources: Image-specific resources with CLIP embeddings
7
7
  - Messages: Communication content
8
+ - Sessions: Conversation sessions (normal or evaluation mode)
9
+ - SharedSessions: Session sharing between users for collaboration
10
+ - Feedback: User feedback on messages/sessions with trace integration
8
11
  - Users: User entities
9
12
  - Files: File metadata and tracking
10
13
  - Moments: Temporal narratives (meetings, coding sessions, conversations)
@@ -19,6 +22,8 @@ All entities inherit from CoreModel and support:
19
22
  - Natural language labels for conversational queries
20
23
  """
21
24
 
25
+ from .domain_resource import DomainResource
26
+ from .feedback import Feedback, FeedbackCategory
22
27
  from .file import File
23
28
  from .image_resource import ImageResource
24
29
  from .message import Message
@@ -27,12 +32,28 @@ from .ontology import Ontology
27
32
  from .ontology_config import OntologyConfig
28
33
  from .resource import Resource
29
34
  from .schema import Schema
35
+ from .session import Session, SessionMode
36
+ from .shared_session import (
37
+ SharedSession,
38
+ SharedSessionCreate,
39
+ SharedWithMeResponse,
40
+ SharedWithMeSummary,
41
+ )
30
42
  from .user import User, UserTier
31
43
 
32
44
  __all__ = [
33
45
  "Resource",
46
+ "DomainResource",
34
47
  "ImageResource",
35
48
  "Message",
49
+ "Session",
50
+ "SessionMode",
51
+ "SharedSession",
52
+ "SharedSessionCreate",
53
+ "SharedWithMeResponse",
54
+ "SharedWithMeSummary",
55
+ "Feedback",
56
+ "FeedbackCategory",
36
57
  "User",
37
58
  "UserTier",
38
59
  "File",
@@ -0,0 +1,38 @@
1
+ """
2
+ DomainResource - Curated internal knowledge in REM.
3
+
4
+ DomainResources are a specialized subclass of Resource for storing curated,
5
+ domain-specific internal knowledge that is not part of general knowledge.
6
+ This includes proprietary information, internal documentation, institutional
7
+ knowledge, and other content that requires more careful curation.
8
+
9
+ Key Differences from Resource:
10
+ - Intended for curated, internal knowledge (not raw ingested content)
11
+ - Higher quality bar - content is reviewed/vetted before ingestion
12
+ - May contain proprietary or sensitive information
13
+ - Subject to different retention/governance policies
14
+
15
+ Use Cases:
16
+ - Internal documentation and procedures
17
+ - Proprietary research and analysis
18
+ - Institutional knowledge bases
19
+ - Domain-specific ontologies and taxonomies
20
+ - Curated best practices and guidelines
21
+ """
22
+
23
+ from .resource import Resource
24
+
25
+
26
+ class DomainResource(Resource):
27
+ """
28
+ Curated domain-specific knowledge resource.
29
+
30
+ Inherits all fields from Resource but stored in a separate table
31
+ (domain_resources) to distinguish curated internal knowledge from
32
+ general ingested content.
33
+
34
+ The schema is identical to Resource, allowing seamless migration
35
+ of content between tables as curation status changes.
36
+ """
37
+
38
+ pass
@@ -0,0 +1,123 @@
1
+ """
2
+ Feedback - User feedback on chat messages and sessions.
3
+
4
+ Feedback allows users to rate and categorize responses, providing
5
+ data for evaluation and model improvement. Feedback can be attached
6
+ to specific messages or entire sessions.
7
+
8
+ Trace Integration:
9
+ - Feedback references trace_id/span_id for OTEL/Phoenix integration
10
+ - Can attach annotations to Phoenix spans for unified observability
11
+
12
+ Predefined Categories (system-defined, extensible):
13
+ - INCOMPLETE: Response lacks expected information
14
+ - INACCURATE: Response contains factual errors
15
+ - POOR_TONE: Inappropriate or unprofessional tone
16
+ - OFF_TOPIC: Response doesn't address the question
17
+ - TOO_VERBOSE: Unnecessarily long response
18
+ - TOO_BRIEF: Insufficiently detailed response
19
+ - HELPFUL: Positive feedback marker
20
+ - EXCELLENT: Exceptionally good response
21
+ """
22
+
23
+ from enum import Enum
24
+ from typing import Any
25
+
26
+ from pydantic import Field
27
+
28
+ from ..core import CoreModel
29
+
30
+
31
+ class FeedbackCategory(str, Enum):
32
+ """Predefined feedback categories (system-defined)."""
33
+
34
+ # Negative categories
35
+ INCOMPLETE = "incomplete"
36
+ INACCURATE = "inaccurate"
37
+ POOR_TONE = "poor_tone"
38
+ OFF_TOPIC = "off_topic"
39
+ TOO_VERBOSE = "too_verbose"
40
+ TOO_BRIEF = "too_brief"
41
+ CONFUSING = "confusing"
42
+ UNSAFE = "unsafe"
43
+
44
+ # Positive categories
45
+ HELPFUL = "helpful"
46
+ EXCELLENT = "excellent"
47
+ ACCURATE = "accurate"
48
+ WELL_WRITTEN = "well_written"
49
+
50
+ # Neutral/Other
51
+ OTHER = "other"
52
+
53
+
54
+ class Feedback(CoreModel):
55
+ """
56
+ User feedback on a message or session.
57
+
58
+ Captures structured feedback including:
59
+ - Rating (1-5 scale or thumbs up/down)
60
+ - Categories (predefined or custom)
61
+ - Free-text comment
62
+ - Trace reference for OTEL/Phoenix integration
63
+
64
+ The feedback can be attached to:
65
+ - A specific message (message_id set)
66
+ - An entire session (session_id set, message_id null)
67
+ """
68
+
69
+ # Target reference (at least one required)
70
+ session_id: str = Field(
71
+ ...,
72
+ description="Session ID this feedback relates to",
73
+ )
74
+ message_id: str | None = Field(
75
+ default=None,
76
+ description="Specific message ID (null for session-level feedback)",
77
+ )
78
+
79
+ # Rating (flexible: 1-5, or -1/1 for thumbs)
80
+ rating: int | None = Field(
81
+ default=None,
82
+ ge=-1,
83
+ le=5,
84
+ description="Rating: -1 (thumbs down), 1 (thumbs up), or 1-5 scale",
85
+ )
86
+
87
+ # Categories (can select multiple)
88
+ categories: list[str] = Field(
89
+ default_factory=list,
90
+ description="Selected feedback categories (from FeedbackCategory or custom)",
91
+ )
92
+
93
+ # Free-text comment
94
+ comment: str | None = Field(
95
+ default=None,
96
+ description="Optional free-text feedback comment",
97
+ )
98
+
99
+ # Trace reference for OTEL/Phoenix integration
100
+ trace_id: str | None = Field(
101
+ default=None,
102
+ description="OTEL trace ID for linking to observability",
103
+ )
104
+ span_id: str | None = Field(
105
+ default=None,
106
+ description="OTEL span ID for specific span feedback",
107
+ )
108
+
109
+ # Phoenix annotation status
110
+ phoenix_synced: bool = Field(
111
+ default=False,
112
+ description="Whether feedback has been synced to Phoenix as annotation",
113
+ )
114
+ phoenix_annotation_id: str | None = Field(
115
+ default=None,
116
+ description="Phoenix annotation ID after sync",
117
+ )
118
+
119
+ # Annotator info
120
+ annotator_kind: str = Field(
121
+ default="HUMAN",
122
+ description="Annotator type: HUMAN, LLM, CODE",
123
+ )
@@ -6,6 +6,11 @@ that can be grouped into conversations or moments.
6
6
 
7
7
  Messages are simpler than Resources but share the same graph connectivity
8
8
  through CoreModel inheritance.
9
+
10
+ Trace Integration:
11
+ - trace_id: OTEL trace ID for linking to observability
12
+ - span_id: OTEL span ID for specific span reference
13
+ - These enable feedback to be attached to Phoenix annotations
9
14
  """
10
15
 
11
16
  from pydantic import Field
@@ -19,6 +24,9 @@ class Message(CoreModel):
19
24
 
20
25
  Represents individual messages in conversations, chats, or other
21
26
  communication contexts. Tenant isolation is provided via CoreModel.tenant_id field.
27
+
28
+ Trace fields (trace_id, span_id) enable integration with OTEL/Phoenix
29
+ for observability and feedback annotation.
22
30
  """
23
31
 
24
32
  content: str = Field(
@@ -27,9 +35,30 @@ class Message(CoreModel):
27
35
  )
28
36
  message_type: str | None = Field(
29
37
  default=None,
30
- description="Message type e.g role",
38
+ description="Message type e.g. role: 'user', 'assistant', 'system', 'tool'",
31
39
  )
32
40
  session_id: str | None = Field(
33
41
  default=None,
34
42
  description="Session identifier for tracking message context",
35
43
  )
44
+ prompt: str | None = Field(
45
+ default=None,
46
+ description="Custom prompt used for this message (if overridden from default)",
47
+ )
48
+ model: str | None = Field(
49
+ default=None,
50
+ description="Model used for generating this message (provider:model format)",
51
+ )
52
+ token_count: int | None = Field(
53
+ default=None,
54
+ description="Token count for this message",
55
+ )
56
+ # OTEL/Phoenix trace integration
57
+ trace_id: str | None = Field(
58
+ default=None,
59
+ description="OTEL trace ID for observability integration",
60
+ )
61
+ span_id: str | None = Field(
62
+ default=None,
63
+ description="OTEL span ID for specific span reference",
64
+ )
@@ -0,0 +1,83 @@
1
+ """
2
+ Session - Conversation sessions in REM.
3
+
4
+ Sessions group related messages together and can have different modes:
5
+ - normal: Standard conversation session
6
+ - evaluation: For LLM evaluation, stores original trace and overridden settings
7
+
8
+ Sessions allow overriding settings like model, temperature, and custom prompts
9
+ for evaluation and experimentation purposes.
10
+ """
11
+
12
+ from enum import Enum
13
+
14
+ from pydantic import Field
15
+
16
+ from ..core import CoreModel
17
+
18
+
19
+ class SessionMode(str, Enum):
20
+ """Session mode types."""
21
+
22
+ NORMAL = "normal"
23
+ EVALUATION = "evaluation"
24
+
25
+
26
+ class Session(CoreModel):
27
+ """
28
+ Conversation session container.
29
+
30
+ Groups messages together and supports different modes for normal conversations
31
+ and evaluation/experimentation scenarios.
32
+
33
+ For evaluation sessions, stores:
34
+ - original_trace_id: Reference to the original session being evaluated
35
+ - settings_overrides: Model, temperature, prompt overrides
36
+ - prompt: Custom prompt being tested
37
+
38
+ Default sessions are lightweight - just a session_id on messages.
39
+ Special sessions store additional metadata for experiments.
40
+ """
41
+
42
+ name: str = Field(
43
+ ...,
44
+ description="Session name/identifier",
45
+ json_schema_extra={"entity_key": True},
46
+ )
47
+ mode: SessionMode = Field(
48
+ default=SessionMode.NORMAL,
49
+ description="Session mode: 'normal' or 'evaluation'",
50
+ )
51
+ description: str | None = Field(
52
+ default=None,
53
+ description="Optional session description",
54
+ )
55
+ # Evaluation-specific fields
56
+ original_trace_id: str | None = Field(
57
+ default=None,
58
+ description="For evaluation mode: ID of the original session/trace being evaluated",
59
+ )
60
+ settings_overrides: dict | None = Field(
61
+ default=None,
62
+ description="Settings overrides (model, temperature, max_tokens, system_prompt)",
63
+ )
64
+ prompt: str | None = Field(
65
+ default=None,
66
+ description="Custom prompt for this session (can override agent prompt)",
67
+ )
68
+ # Agent context
69
+ agent_schema_uri: str | None = Field(
70
+ default=None,
71
+ description="Agent schema used for this session",
72
+ )
73
+ # Summary stats (updated as session progresses)
74
+ message_count: int = Field(
75
+ default=0,
76
+ description="Number of messages in this session",
77
+ )
78
+ total_tokens: int | None = Field(
79
+ default=None,
80
+ description="Total tokens used in this session",
81
+ )
82
+
83
+ model_config = {"use_enum_values": True}