projectdavid-orm 1.12.0__tar.gz → 1.13.1__tar.gz

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.
Files changed (29) hide show
  1. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/CHANGELOG.md +15 -0
  2. {projectdavid_orm-1.12.0/src/projectdavid_orm.egg-info → projectdavid_orm-1.13.1}/PKG-INFO +3 -1
  3. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/pyproject.toml +3 -1
  4. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_orm/__init__.py +2 -0
  5. projectdavid_orm-1.13.1/src/projectdavid_orm/enums.py +20 -0
  6. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_orm/ormInterface.py +2 -0
  7. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_orm/projectdavid_orm/models.py +75 -8
  8. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1/src/projectdavid_orm.egg-info}/PKG-INFO +3 -1
  9. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_orm.egg-info/SOURCES.txt +4 -1
  10. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_orm.egg-info/requires.txt +2 -0
  11. projectdavid_orm-1.13.1/tests/test_runtime_dependency_boundary.py +57 -0
  12. projectdavid_orm-1.13.1/tests/test_scratchpad_public_exports.py +6 -0
  13. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/LICENSE +0 -0
  14. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/MANIFEST.in +0 -0
  15. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/README.md +0 -0
  16. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/setup.cfg +0 -0
  17. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_common.egg-info/PKG-INFO +0 -0
  18. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_common.egg-info/SOURCES.txt +0 -0
  19. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_common.egg-info/dependency_links.txt +0 -0
  20. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_common.egg-info/entry_points.txt +0 -0
  21. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_common.egg-info/requires.txt +0 -0
  22. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_common.egg-info/top_level.txt +0 -0
  23. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_orm/constants/__init__.py +0 -0
  24. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_orm/projectdavid_orm/__init__.py +0 -0
  25. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_orm/projectdavid_orm/base.py +0 -0
  26. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_orm/utilities/__init__.py +0 -0
  27. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_orm.egg-info/dependency_links.txt +0 -0
  28. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/src/projectdavid_orm.egg-info/top_level.txt +0 -0
  29. {projectdavid_orm-1.12.0 → projectdavid_orm-1.13.1}/tests/test_clients.py +0 -0
@@ -1,3 +1,18 @@
1
+ ## [1.13.1](https://github.com/project-david-ai/projectdavid-orm/compare/v1.13.0...v1.13.1) (2026-09-29)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **deps:** decouple ORM from Common runtime ([4e1ca2e](https://github.com/project-david-ai/projectdavid-orm/commit/4e1ca2e9b2a1b84d014fc0447849964bf974daf5))
7
+ * **exports:** expose Scratchpad through public ORM API ([205ae03](https://github.com/project-david-ai/projectdavid-orm/commit/205ae03ebe801701d9c19ce83e1b59764a8ab93b))
8
+
9
+ # [1.13.0](https://github.com/project-david-ai/projectdavid-orm/compare/v1.12.0...v1.13.0) (2026-09-29)
10
+
11
+
12
+ ### Features
13
+
14
+ * **schema:** add thread-associated scratchpad resource ([5e8d87c](https://github.com/project-david-ai/projectdavid-orm/commit/5e8d87c6641b4eed245feb42c373c91bd643cfc3))
15
+
1
16
  # [1.12.0](https://github.com/project-david-ai/projectdavid-orm/compare/v1.11.0...v1.12.0) (2026-09-28)
2
17
 
3
18
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: projectdavid-orm
3
- Version: 1.12.0
3
+ Version: 1.13.1
4
4
  Summary: Shared SQLAlchemy ORM models for ProjectDavid services
5
5
  Author-email: "Francis N." <engineering@projectdavid.co.uk>
6
6
  License: PolyForm Noncommercial License 1.0.0
@@ -15,6 +15,8 @@ Requires-Dist: sqlalchemy>=2.0
15
15
  Requires-Dist: pymysql
16
16
  Requires-Dist: pydantic>=2.0
17
17
  Requires-Dist: python-dotenv
18
+ Requires-Dist: passlib==1.7.4
19
+ Requires-Dist: bcrypt==4.0.1
18
20
  Provides-Extra: dev
19
21
  Requires-Dist: pytest; extra == "dev"
20
22
  Requires-Dist: black==24.1.1; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "projectdavid-orm"
7
- version = "1.12.0"
7
+ version = "1.13.1"
8
8
  description = "Shared SQLAlchemy ORM models for ProjectDavid services"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -19,6 +19,8 @@ dependencies = [
19
19
  "pymysql",
20
20
  "pydantic>=2.0",
21
21
  "python-dotenv",
22
+ "passlib==1.7.4",
23
+ "bcrypt==4.0.1",
22
24
  ]
23
25
 
24
26
  [project.optional-dependencies]
@@ -15,6 +15,7 @@ from .ormInterface import (
15
15
  OrmInterface,
16
16
  Run,
17
17
  Sandbox,
18
+ Scratchpad,
18
19
  Thread,
19
20
  TrainingJob,
20
21
  User,
@@ -36,6 +37,7 @@ __all__ = [
36
37
  "Assistant",
37
38
  "Action",
38
39
  "Sandbox",
40
+ "Scratchpad",
39
41
  # File system
40
42
  "File",
41
43
  "FileStorage",
@@ -0,0 +1,20 @@
1
+ from enum import Enum
2
+
3
+
4
+ class StatusEnum(str, Enum):
5
+ """Canonical persistence status values used by ORM models."""
6
+
7
+ deleted = "deleted"
8
+ active = "active"
9
+ queued = "queued"
10
+ in_progress = "in_progress"
11
+ pending_action = "action_required"
12
+ completed = "completed"
13
+ failed = "failed"
14
+ cancelling = "cancelling"
15
+ cancelled = "cancelled"
16
+ pending = "pending"
17
+ processing = "processing"
18
+ expired = "expired"
19
+ retrying = "retrying"
20
+ offline = "offline"
@@ -30,6 +30,8 @@ class OrmInterface:
30
30
  Action = Action
31
31
  # Sandbox model
32
32
  Sandbox = Sandbox
33
+ # Scratchpad model
34
+ Scratchpad = Scratchpad
33
35
  # File model
34
36
  File = File
35
37
  # FileStorage model
@@ -3,9 +3,6 @@ import time
3
3
  from datetime import datetime
4
4
 
5
5
  from passlib.context import CryptContext
6
- from projectdavid_common import ValidationInterface
7
- from projectdavid_common.schemas.enums import StatusEnum
8
- from projectdavid_common.utilities.logging_service import LoggingUtility
9
6
  from sqlalchemy import JSON, BigInteger, Boolean, Column, DateTime
10
7
  from sqlalchemy import Enum as SAEnum
11
8
  from sqlalchemy import (
@@ -22,11 +19,9 @@ from sqlalchemy import (
22
19
  from sqlalchemy.ext.mutable import MutableDict
23
20
  from sqlalchemy.orm import joinedload, relationship
24
21
 
25
- from .base import Base
26
-
27
- logger = LoggingUtility()
22
+ from projectdavid_orm.enums import StatusEnum
28
23
 
29
- validation = ValidationInterface
24
+ from .base import Base
30
25
 
31
26
  # --- Association Tables ---
32
27
 
@@ -195,6 +190,12 @@ class User(Base):
195
190
  lazy="select",
196
191
  )
197
192
 
193
+ scratchpads = relationship(
194
+ "Scratchpad",
195
+ back_populates="owner",
196
+ lazy="select",
197
+ )
198
+
198
199
  __table_args__ = (
199
200
  UniqueConstraint(
200
201
  "oauth_provider", "provider_user_id", name="uq_user_oauth_provider_id"
@@ -269,6 +270,72 @@ class Thread(Base):
269
270
  )
270
271
  owner = relationship("User", foreign_keys=[owner_id], lazy="select")
271
272
 
273
+ scratchpad = relationship(
274
+ "Scratchpad",
275
+ back_populates="thread",
276
+ uselist=False,
277
+ cascade="all, delete-orphan",
278
+ single_parent=True,
279
+ lazy="select",
280
+ )
281
+
282
+
283
+ class Scratchpad(Base):
284
+ """Tenant-owned working-memory resource associated with one thread."""
285
+
286
+ __tablename__ = "scratchpads"
287
+
288
+ id = Column(String(64), primary_key=True, index=True)
289
+
290
+ owner_id = Column(
291
+ String(64),
292
+ ForeignKey("users.id", ondelete="CASCADE"),
293
+ nullable=False,
294
+ )
295
+
296
+ thread_id = Column(
297
+ String(64),
298
+ ForeignKey("threads.id", ondelete="CASCADE"),
299
+ nullable=False,
300
+ )
301
+
302
+ created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
303
+ updated_at = Column(
304
+ DateTime,
305
+ nullable=False,
306
+ default=datetime.utcnow,
307
+ onupdate=datetime.utcnow,
308
+ )
309
+
310
+ meta_data = Column(
311
+ MutableDict.as_mutable(JSON),
312
+ nullable=False,
313
+ default=dict,
314
+ )
315
+
316
+ owner = relationship(
317
+ "User",
318
+ back_populates="scratchpads",
319
+ lazy="select",
320
+ )
321
+
322
+ thread = relationship(
323
+ "Thread",
324
+ back_populates="scratchpad",
325
+ lazy="select",
326
+ )
327
+
328
+ __table_args__ = (
329
+ UniqueConstraint(
330
+ "thread_id",
331
+ name="uq_scratchpads_thread_id",
332
+ ),
333
+ Index(
334
+ "idx_scratchpads_owner_id",
335
+ "owner_id",
336
+ ),
337
+ )
338
+
272
339
 
273
340
  class Message(Base):
274
341
  __tablename__ = "messages"
@@ -356,7 +423,7 @@ class Run(Base):
356
423
  required_action = Column(String(256), nullable=True)
357
424
  response_format = Column(String(64), nullable=True)
358
425
 
359
- status = Column(SAEnum(validation.StatusEnum), nullable=False)
426
+ status = Column(SAEnum(StatusEnum), nullable=False)
360
427
 
361
428
  thread_id = Column(String(64), nullable=False)
362
429
  tool_choice = Column(String(64), nullable=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: projectdavid-orm
3
- Version: 1.12.0
3
+ Version: 1.13.1
4
4
  Summary: Shared SQLAlchemy ORM models for ProjectDavid services
5
5
  Author-email: "Francis N." <engineering@projectdavid.co.uk>
6
6
  License: PolyForm Noncommercial License 1.0.0
@@ -15,6 +15,8 @@ Requires-Dist: sqlalchemy>=2.0
15
15
  Requires-Dist: pymysql
16
16
  Requires-Dist: pydantic>=2.0
17
17
  Requires-Dist: python-dotenv
18
+ Requires-Dist: passlib==1.7.4
19
+ Requires-Dist: bcrypt==4.0.1
18
20
  Provides-Extra: dev
19
21
  Requires-Dist: pytest; extra == "dev"
20
22
  Requires-Dist: black==24.1.1; extra == "dev"
@@ -10,6 +10,7 @@ src/projectdavid_common.egg-info/entry_points.txt
10
10
  src/projectdavid_common.egg-info/requires.txt
11
11
  src/projectdavid_common.egg-info/top_level.txt
12
12
  src/projectdavid_orm/__init__.py
13
+ src/projectdavid_orm/enums.py
13
14
  src/projectdavid_orm/ormInterface.py
14
15
  src/projectdavid_orm.egg-info/PKG-INFO
15
16
  src/projectdavid_orm.egg-info/SOURCES.txt
@@ -21,4 +22,6 @@ src/projectdavid_orm/projectdavid_orm/__init__.py
21
22
  src/projectdavid_orm/projectdavid_orm/base.py
22
23
  src/projectdavid_orm/projectdavid_orm/models.py
23
24
  src/projectdavid_orm/utilities/__init__.py
24
- tests/test_clients.py
25
+ tests/test_clients.py
26
+ tests/test_runtime_dependency_boundary.py
27
+ tests/test_scratchpad_public_exports.py
@@ -2,6 +2,8 @@ sqlalchemy>=2.0
2
2
  pymysql
3
3
  pydantic>=2.0
4
4
  python-dotenv
5
+ passlib==1.7.4
6
+ bcrypt==4.0.1
5
7
 
6
8
  [dev]
7
9
  pytest
@@ -0,0 +1,57 @@
1
+ from pathlib import Path
2
+
3
+ from projectdavid_orm.enums import StatusEnum
4
+ from projectdavid_orm.projectdavid_orm.models import ApiKey
5
+
6
+ EXPECTED_STATUS_VALUES = {
7
+ "deleted": "deleted",
8
+ "active": "active",
9
+ "queued": "queued",
10
+ "in_progress": "in_progress",
11
+ "pending_action": "action_required",
12
+ "completed": "completed",
13
+ "failed": "failed",
14
+ "cancelling": "cancelling",
15
+ "cancelled": "cancelled",
16
+ "pending": "pending",
17
+ "processing": "processing",
18
+ "expired": "expired",
19
+ "retrying": "retrying",
20
+ "offline": "offline",
21
+ }
22
+
23
+
24
+ def test_status_enum_persistence_contract_is_preserved():
25
+ assert {item.name: item.value for item in StatusEnum} == EXPECTED_STATUS_VALUES
26
+
27
+
28
+ def test_models_have_no_runtime_dependency_on_common():
29
+ models_path = (
30
+ Path(__file__).parents[1]
31
+ / "src"
32
+ / "projectdavid_orm"
33
+ / "projectdavid_orm"
34
+ / "models.py"
35
+ )
36
+
37
+ source = models_path.read_text(encoding="utf-8")
38
+
39
+ assert "projectdavid_common" not in source
40
+ assert "ValidationInterface" not in source
41
+ assert "LoggingUtility" not in source
42
+
43
+
44
+ def test_bcrypt_backend_hashes_and_verifies_api_keys():
45
+ plain = "project-david-runtime-dependency-probe"
46
+
47
+ hashed = ApiKey.hash_key(plain)
48
+
49
+ assert hashed != plain
50
+
51
+ api_key = ApiKey(
52
+ key_name="dependency-probe",
53
+ hashed_key=hashed,
54
+ )
55
+
56
+ assert api_key.verify_key(plain)
57
+ assert not api_key.verify_key(plain + "-invalid")
@@ -0,0 +1,6 @@
1
+ from projectdavid_orm import OrmInterface, Scratchpad
2
+
3
+
4
+ def test_scratchpad_is_exposed_through_public_orm_api():
5
+ assert Scratchpad.__tablename__ == "scratchpads"
6
+ assert OrmInterface.Scratchpad is Scratchpad