agent-learning 0.4.2__tar.gz → 0.4.3__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 (96) hide show
  1. {agent_learning-0.4.2/src/agent_learning.egg-info → agent_learning-0.4.3}/PKG-INFO +5 -5
  2. agent_learning-0.4.3/PYPI.md +15 -0
  3. {agent_learning-0.4.2 → agent_learning-0.4.3}/README.md +9 -9
  4. {agent_learning-0.4.2 → agent_learning-0.4.3}/pyproject.toml +1 -1
  5. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/_version.py +1 -1
  6. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/cli.py +33 -2
  7. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/storage/base.py +3 -1
  8. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/storage/cosmos.py +8 -0
  9. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/storage/local.py +4 -0
  10. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/storage/memory.py +4 -0
  11. {agent_learning-0.4.2 → agent_learning-0.4.3/src/agent_learning.egg-info}/PKG-INFO +5 -5
  12. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_cli.py +90 -1
  13. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_storage_local.py +3 -0
  14. agent_learning-0.4.2/PYPI.md +0 -15
  15. {agent_learning-0.4.2 → agent_learning-0.4.3}/LICENSE +0 -0
  16. {agent_learning-0.4.2 → agent_learning-0.4.3}/setup.cfg +0 -0
  17. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/__init__.py +0 -0
  18. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/capture.py +0 -0
  19. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/__init__.py +0 -0
  20. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/base.py +0 -0
  21. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/router.py +0 -0
  22. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/__init__.py +0 -0
  23. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/_base.py +0 -0
  24. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/adherence.py +0 -0
  25. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/completion.py +0 -0
  26. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/classifiers/scorers/intent.py +0 -0
  27. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/config.py +0 -0
  28. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/learners/__init__.py +0 -0
  29. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/learners/base.py +0 -0
  30. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/learners/reinforce.py +0 -0
  31. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/__init__.py +0 -0
  32. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/base.py +0 -0
  33. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/intent_resolution.py +0 -0
  34. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/local.py +0 -0
  35. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/registry.py +0 -0
  36. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/task_adherence.py +0 -0
  37. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/metrics/task_completion.py +0 -0
  38. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/policy/__init__.py +0 -0
  39. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/policy/base.py +0 -0
  40. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/policy/contextual_softmax.py +0 -0
  41. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/policy/softmax_bandit.py +0 -0
  42. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/py.typed +0 -0
  43. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/rewards/__init__.py +0 -0
  44. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/rewards/shaping.py +0 -0
  45. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/rewards/writer.py +0 -0
  46. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/__init__.py +0 -0
  47. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/base.py +0 -0
  48. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/llm/__init__.py +0 -0
  49. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/llm/_base.py +0 -0
  50. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/llm/adherence.py +0 -0
  51. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/llm/completion.py +0 -0
  52. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/llm/intent.py +0 -0
  53. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp/__init__.py +0 -0
  54. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp/_base.py +0 -0
  55. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp/adherence.py +0 -0
  56. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp/completion.py +0 -0
  57. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp/intent.py +0 -0
  58. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/__init__.py +0 -0
  59. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/_base.py +0 -0
  60. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/adherence.py +0 -0
  61. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/completion.py +0 -0
  62. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/nlp_text/intent.py +0 -0
  63. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/slm/__init__.py +0 -0
  64. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/slm/_base.py +0 -0
  65. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/slm/adherence.py +0 -0
  66. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/slm/completion.py +0 -0
  67. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/slm/intent.py +0 -0
  68. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/stdlib/__init__.py +0 -0
  69. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/stdlib/_text.py +0 -0
  70. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/stdlib/adherence.py +0 -0
  71. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/stdlib/completion.py +0 -0
  72. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/scorers/stdlib/intent.py +0 -0
  73. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/storage/__init__.py +0 -0
  74. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/training/__init__.py +0 -0
  75. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/training/runner.py +0 -0
  76. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning/types.py +0 -0
  77. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning.egg-info/SOURCES.txt +0 -0
  78. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning.egg-info/dependency_links.txt +0 -0
  79. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning.egg-info/entry_points.txt +0 -0
  80. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning.egg-info/requires.txt +0 -0
  81. {agent_learning-0.4.2 → agent_learning-0.4.3}/src/agent_learning.egg-info/top_level.txt +0 -0
  82. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_capture.py +0 -0
  83. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_contextual_policy.py +0 -0
  84. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_default_store.py +0 -0
  85. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_end_to_end.py +0 -0
  86. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_learner.py +0 -0
  87. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_metrics_local.py +0 -0
  88. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_policy.py +0 -0
  89. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_scorers.py +0 -0
  90. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_scorers_llm.py +0 -0
  91. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_scorers_nlp_text.py +0 -0
  92. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_scorers_slm.py +0 -0
  93. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_scorers_stdlib.py +0 -0
  94. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_shaping.py +0 -0
  95. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_storage_memory.py +0 -0
  96. {agent_learning-0.4.2 → agent_learning-0.4.3}/tests/test_types.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-learning
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Summary: Native, in-process reinforcement learning SDK for AI agents — in-memory and local-file storage by default.
5
5
  Author: Chris Tava
6
6
  License: MIT License
@@ -67,16 +67,16 @@ Dynamic: license-file
67
67
 
68
68
  # agent-learning
69
69
 
70
- Native reinforcement learning SDK for AI agents. An in-process learner optimizes a small, interpretable policy over discrete agent choices (understand intent and complete task by choosing the right outcome).
70
+ Native reinforcement learning SDK for AI agents. An in-process Learner optimizes a small, interpretable TaskPolicy over discrete agent choices (understand intent and complete task by choosing the right outcome).
71
71
 
72
72
  ## How it works
73
73
 
74
74
  The SDK improves agents without LLM weight fine-tuning. There are no GPU fine-tune jobs and no opaque update cycles — just three pieces that run in your existing Python process:
75
75
 
76
- 1. The **policy** is a softmax distribution over `N` discrete actions (e.g., "take action A", "take action B", "take action C"). It lives in Python and updates in milliseconds.
76
+ 1. **TaskPolicy** is a softmax distribution over `N` discrete actions (e.g., "take action A", "take action B", "take action C"). It lives in Python and updates in milliseconds.
77
77
 
78
- 2. Each episode is **evaluated** on-device by three stdlib scorers for intent resolution, task adherence, and task completion. Their scores are combined into a single scalar reward with no scoring endpoint or environment variables required. Azure AI evaluators remain available as an opt-in.
78
+ 2. **Score** evaluates each episode on-device with three stdlib scorers for intent resolution, task adherence, and task completion. Their scores are combined into a single scalar reward with no scoring endpoint or environment variables required. Azure AI evaluators remain available as an opt-in.
79
79
 
80
- 3. A **REINFORCE-with-baseline** learner updates the policy logits directly from logged episodes. Updates are tiny gradient steps that run on local compute and persist through a pluggable store — in-memory or local files by default, with Azure Cosmos DB optional.
80
+ 3. **Learner** applies REINFORCE-with-baseline to update TaskPolicy logits directly from logged episodes. Updates are tiny gradient steps that run on local compute and persist through a pluggable store — in-memory or local files by default, with Azure Cosmos DB optional.
81
81
 
82
82
  Every episode, reward, run, and deployment is captured by the configured store — in-memory or local files by default, or Azure Cosmos DB — giving you a complete lineage and audit trail of how the policy evolved over time.
@@ -0,0 +1,15 @@
1
+ # agent-learning
2
+
3
+ Native reinforcement learning SDK for AI agents. An in-process Learner optimizes a small, interpretable TaskPolicy over discrete agent choices (understand intent and complete task by choosing the right outcome).
4
+
5
+ ## How it works
6
+
7
+ The SDK improves agents without LLM weight fine-tuning. There are no GPU fine-tune jobs and no opaque update cycles — just three pieces that run in your existing Python process:
8
+
9
+ 1. **TaskPolicy** is a softmax distribution over `N` discrete actions (e.g., "take action A", "take action B", "take action C"). It lives in Python and updates in milliseconds.
10
+
11
+ 2. **Score** evaluates each episode on-device with three stdlib scorers for intent resolution, task adherence, and task completion. Their scores are combined into a single scalar reward with no scoring endpoint or environment variables required. Azure AI evaluators remain available as an opt-in.
12
+
13
+ 3. **Learner** applies REINFORCE-with-baseline to update TaskPolicy logits directly from logged episodes. Updates are tiny gradient steps that run on local compute and persist through a pluggable store — in-memory or local files by default, with Azure Cosmos DB optional.
14
+
15
+ Every episode, reward, run, and deployment is captured by the configured store — in-memory or local files by default, or Azure Cosmos DB — giving you a complete lineage and audit trail of how the policy evolved over time.
@@ -1,30 +1,30 @@
1
1
  # agent-learning
2
2
 
3
3
  Native reinforcement learning SDK for AI agents. An in-process
4
- learner optimizes a small, interpretable policy over discrete agent choices (e.g., "take action A", "take action B", "take action C") using on-device evaluation scores as the reward
4
+ Learner optimizes a small, interpretable TaskPolicy over discrete agent choices (e.g., "take action A", "take action B", "take action C") using on-device evaluation scores as the reward
5
5
  signal by default.
6
6
 
7
7
  <p align="center">
8
- <img src="images/agent-learning-loop.svg" alt="Animated loop: Policy chooses an action, Score evaluates the episode, and Learner updates the policy" width="960" style="max-width:100%; height:auto;" />
8
+ <img src="images/agent-learning-loop.svg" alt="Animated TaskPolicy Score Learner loop: TaskPolicy chooses a task action, Score evaluates the episode, and Learner updates TaskPolicy" width="960" style="max-width:100%; height:auto;" />
9
9
  </p>
10
10
 
11
11
  ## How it works
12
12
 
13
13
  The SDK improves agents without LLM weight fine-tuning. There are no GPU fine-tune jobs and no opaque update cycles — just three pieces that run in your existing Python process:
14
14
 
15
- 1. The **policy** is a softmax distribution over `N` discrete
15
+ 1. **TaskPolicy** is a softmax distribution over `N` discrete
16
16
  actions (e.g., "take action A", "take action B", "take action C"). It lives in Python and updates in milliseconds.
17
17
 
18
- <img src="images/0f85e08d0c47cd01.png" alt="Policy selects one of N discrete actions" width="360" style="max-width:100%; height:auto;" />
18
+ <img src="images/0f85e08d0c47cd01.png" alt="TaskPolicy selects one of N discrete actions" width="360" style="max-width:100%; height:auto;" />
19
19
 
20
- 2. Each episode is **evaluated** locally by three stdlib scorers for intent
20
+ 2. **Score** evaluates each episode locally with three stdlib scorers for intent
21
21
  resolution, task adherence, and task completion. Their scores are combined
22
22
  into one scalar reward. No scoring endpoint or environment variable is
23
23
  required. Configured Azure AI evaluators remain available as an opt-in.
24
24
 
25
25
  <img src="images/246d112f995b785a.png" alt="Three evaluator scores feed a single scalar reward" width="360" style="max-width:100%; height:auto;" />
26
26
 
27
- 3. A **Reinforce-with-baseline** learner updates the policy logits
27
+ 3. **Learner** applies REINFORCE-with-baseline to update TaskPolicy logits
28
28
  directly from stored episodes. Updates are tiny gradient steps
29
29
  that run on local compute and persist through a pluggable store — in-memory
30
30
  or local files by default, with Azure Cosmos DB optional.
@@ -72,11 +72,11 @@ The `agent-learn` CLI provides the current task-learning-loop operations:
72
72
  ```text
73
73
  agent-learn list
74
74
  agent-learn tasks-list <agent_id>
75
- agent-learn task-episodes-count <agent_id> [--task-id <task_id>]
76
- agent-learn task-episodes-list <agent_id> [--task-id <task_id>] [--limit <1-500>] [--include-incomplete]
75
+ agent-learn task-episodes-count <agent_id> [--task-id <task_id>] [--start-date <date>] [--end-date <date>]
76
+ agent-learn task-episodes-list <agent_id> [--task-id <task_id>] [--limit <1-500>] [--include-incomplete] [--start-date <date>] [--end-date <date>]
77
77
  agent-learn task-policy-init --agent-id <agent_id> --task-id <task_id> --actions ./actions.json
78
78
  agent-learn task-episode-register --agent-id <agent_id> --task-id <task_id> --episode ./episode.json
79
79
  agent-learn score --agent-id <agent_id> [--task-id <task_id>] [--limit <1-500>]
80
- agent-learn train --agent-id <agent_id> [--task-id <task_id>] [--limit <1-500>] [--start-date <date>] [--end-date <date>] [--skip-scoring]
80
+ agent-learn train --agent-id <agent_id> [--task-id <task_id>] [--limit <1-500>] [--min-episodes <1-500>] [--start-date <date>] [--end-date <date>] [--skip-scoring]
81
81
  agent-learn task-policy --agent-id <agent_id> --task-id <task_id>
82
82
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "agent-learning"
7
- version = "0.4.2"
7
+ version = "0.4.3"
8
8
  description = "Native, in-process reinforcement learning SDK for AI agents — in-memory and local-file storage by default."
9
9
  readme = "PYPI.md"
10
10
  requires-python = ">=3.10"
@@ -1,3 +1,3 @@
1
1
  """Package version."""
2
2
 
3
- __version__ = "0.4.2"
3
+ __version__ = "0.4.3"
@@ -8,6 +8,7 @@ import logging
8
8
  import sys
9
9
  import uuid
10
10
  from collections import Counter
11
+ from datetime import datetime, timezone
11
12
  from typing import Any
12
13
 
13
14
  from .policy.softmax_bandit import SoftmaxPolicy
@@ -27,6 +28,16 @@ def _episode_limit(value: str) -> int:
27
28
  return limit
28
29
 
29
30
 
31
+ def _iso_date(value: str) -> str:
32
+ try:
33
+ parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
34
+ except ValueError as exc:
35
+ raise argparse.ArgumentTypeError(f"invalid ISO 8601 date: {value!r}") from exc
36
+ if parsed.tzinfo is None:
37
+ parsed = parsed.replace(tzinfo=timezone.utc)
38
+ return parsed.astimezone(timezone.utc).isoformat()
39
+
40
+
30
41
  def _build_arg_parser() -> argparse.ArgumentParser:
31
42
  parser = argparse.ArgumentParser(prog="agent-learn", description="Native RL CLI for AI agents.")
32
43
  sub = parser.add_subparsers(dest="command", required=True)
@@ -42,6 +53,8 @@ def _build_arg_parser() -> argparse.ArgumentParser:
42
53
  )
43
54
  count.add_argument("agent_id")
44
55
  count.add_argument("--task-id")
56
+ count.add_argument("--start-date", type=_iso_date)
57
+ count.add_argument("--end-date", type=_iso_date)
45
58
 
46
59
  episodes = sub.add_parser(
47
60
  "task-episodes-list",
@@ -51,13 +64,16 @@ def _build_arg_parser() -> argparse.ArgumentParser:
51
64
  episodes.add_argument("--task-id")
52
65
  episodes.add_argument("--limit", type=_episode_limit, default=_MAX_EPISODES)
53
66
  episodes.add_argument("--include-incomplete", action="store_true")
67
+ episodes.add_argument("--start-date", type=_iso_date)
68
+ episodes.add_argument("--end-date", type=_iso_date)
54
69
 
55
70
  train = sub.add_parser("train", help="Run one offline learning batch.")
56
71
  train.add_argument("--agent-id", required=True)
57
72
  train.add_argument("--task-id")
58
73
  train.add_argument("--limit", type=_episode_limit, default=200)
59
- train.add_argument("--start-date")
60
- train.add_argument("--end-date")
74
+ train.add_argument("--min-episodes", type=_episode_limit, default=1)
75
+ train.add_argument("--start-date", type=_iso_date)
76
+ train.add_argument("--end-date", type=_iso_date)
61
77
  train.add_argument(
62
78
  "--skip-scoring",
63
79
  action="store_true",
@@ -118,6 +134,8 @@ def _cmd_agents_episodes_count(args: argparse.Namespace) -> int:
118
134
  args.agent_id,
119
135
  task_id=args.task_id,
120
136
  full_only=True,
137
+ start_date=args.start_date,
138
+ end_date=args.end_date,
121
139
  )
122
140
  print(count)
123
141
  return 0
@@ -129,6 +147,8 @@ def _cmd_agents_episodes_list(args: argparse.Namespace) -> int:
129
147
  args.agent_id,
130
148
  task_id=args.task_id,
131
149
  limit=_MAX_EPISODES,
150
+ start_date=args.start_date,
151
+ end_date=args.end_date,
132
152
  )
133
153
  if not args.include_incomplete:
134
154
  episodes = [episode for episode in episodes if episode.is_full]
@@ -183,6 +203,17 @@ def _cmd_train(args: argparse.Namespace) -> int:
183
203
  if episode_limit == 0:
184
204
  skipped.append({"task_id": task_id, "reason": "no episodes in selected batch"})
185
205
  continue
206
+ if episode_limit < args.min_episodes:
207
+ skipped.append(
208
+ {
209
+ "task_id": task_id,
210
+ "reason": (
211
+ f"selected batch has {episode_limit} episodes; "
212
+ f"minimum is {args.min_episodes}"
213
+ ),
214
+ }
215
+ )
216
+ continue
186
217
  policy = SoftmaxPolicy.from_snapshot(snapshot)
187
218
  runner = LearningRunner(store=store, policy=policy)
188
219
  run = runner.run_offline_batch(
@@ -66,8 +66,10 @@ class LearningStore(ABC):
66
66
  *,
67
67
  task_id: Optional[str] = None,
68
68
  full_only: bool = False,
69
+ start_date: Optional[str] = None,
70
+ end_date: Optional[str] = None,
69
71
  ) -> int:
70
- """Count episodes, optionally limiting the count to full episodes."""
72
+ """Count episodes filtered by task, completeness, or time window."""
71
73
 
72
74
  # ---- Metric results -------------------------------------------
73
75
 
@@ -272,6 +272,8 @@ class CosmosStore(LearningStore):
272
272
  *,
273
273
  task_id: Optional[str] = None,
274
274
  full_only: bool = False,
275
+ start_date: Optional[str] = None,
276
+ end_date: Optional[str] = None,
275
277
  ) -> int:
276
278
  clauses = ["c.agent_id = @agent_id"]
277
279
  params: List[Dict[str, Any]] = [{"name": "@agent_id", "value": agent_id}]
@@ -281,6 +283,12 @@ class CosmosStore(LearningStore):
281
283
  else:
282
284
  clauses.append("c.task_id = @task_id")
283
285
  params.append({"name": "@task_id", "value": task_id})
286
+ if start_date:
287
+ clauses.append("c.created_at >= @start_date")
288
+ params.append({"name": "@start_date", "value": start_date})
289
+ if end_date:
290
+ clauses.append("c.created_at <= @end_date")
291
+ params.append({"name": "@end_date", "value": end_date})
284
292
  if full_only:
285
293
  clauses.extend(
286
294
  [
@@ -212,12 +212,16 @@ class LocalFileStore(LearningStore):
212
212
  *,
213
213
  task_id: Optional[str] = None,
214
214
  full_only: bool = False,
215
+ start_date: Optional[str] = None,
216
+ end_date: Optional[str] = None,
215
217
  ) -> int:
216
218
  return sum(
217
219
  1
218
220
  for doc in self._read_dir_docs("episodes", agent_id)
219
221
  if (task_id is None or doc.get("task_id", "default") == task_id)
220
222
  and (not full_only or Episode.from_dict(doc).is_full)
223
+ and (start_date is None or doc.get("created_at", "") >= start_date)
224
+ and (end_date is None or doc.get("created_at", "") <= end_date)
221
225
  )
222
226
 
223
227
  # ------------------------------------------------------------------
@@ -93,6 +93,8 @@ class InMemoryStore(LearningStore):
93
93
  *,
94
94
  task_id: Optional[str] = None,
95
95
  full_only: bool = False,
96
+ start_date: Optional[str] = None,
97
+ end_date: Optional[str] = None,
96
98
  ) -> int:
97
99
  return sum(
98
100
  1
@@ -100,6 +102,8 @@ class InMemoryStore(LearningStore):
100
102
  if episode.agent_id == agent_id
101
103
  and (task_id is None or episode.task_id == task_id)
102
104
  and (not full_only or episode.is_full)
105
+ and (start_date is None or episode.created_at >= start_date)
106
+ and (end_date is None or episode.created_at <= end_date)
103
107
  )
104
108
 
105
109
  # ---- Metric results -------------------------------------------
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-learning
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Summary: Native, in-process reinforcement learning SDK for AI agents — in-memory and local-file storage by default.
5
5
  Author: Chris Tava
6
6
  License: MIT License
@@ -67,16 +67,16 @@ Dynamic: license-file
67
67
 
68
68
  # agent-learning
69
69
 
70
- Native reinforcement learning SDK for AI agents. An in-process learner optimizes a small, interpretable policy over discrete agent choices (understand intent and complete task by choosing the right outcome).
70
+ Native reinforcement learning SDK for AI agents. An in-process Learner optimizes a small, interpretable TaskPolicy over discrete agent choices (understand intent and complete task by choosing the right outcome).
71
71
 
72
72
  ## How it works
73
73
 
74
74
  The SDK improves agents without LLM weight fine-tuning. There are no GPU fine-tune jobs and no opaque update cycles — just three pieces that run in your existing Python process:
75
75
 
76
- 1. The **policy** is a softmax distribution over `N` discrete actions (e.g., "take action A", "take action B", "take action C"). It lives in Python and updates in milliseconds.
76
+ 1. **TaskPolicy** is a softmax distribution over `N` discrete actions (e.g., "take action A", "take action B", "take action C"). It lives in Python and updates in milliseconds.
77
77
 
78
- 2. Each episode is **evaluated** on-device by three stdlib scorers for intent resolution, task adherence, and task completion. Their scores are combined into a single scalar reward with no scoring endpoint or environment variables required. Azure AI evaluators remain available as an opt-in.
78
+ 2. **Score** evaluates each episode on-device with three stdlib scorers for intent resolution, task adherence, and task completion. Their scores are combined into a single scalar reward with no scoring endpoint or environment variables required. Azure AI evaluators remain available as an opt-in.
79
79
 
80
- 3. A **REINFORCE-with-baseline** learner updates the policy logits directly from logged episodes. Updates are tiny gradient steps that run on local compute and persist through a pluggable store — in-memory or local files by default, with Azure Cosmos DB optional.
80
+ 3. **Learner** applies REINFORCE-with-baseline to update TaskPolicy logits directly from logged episodes. Updates are tiny gradient steps that run on local compute and persist through a pluggable store — in-memory or local files by default, with Azure Cosmos DB optional.
81
81
 
82
82
  Every episode, reward, run, and deployment is captured by the configured store — in-memory or local files by default, or Azure Cosmos DB — giving you a complete lineage and audit trail of how the policy evolved over time.
@@ -54,6 +54,48 @@ def test_discovery_and_full_episode_count(monkeypatch, capsys) -> None:
54
54
  assert capsys.readouterr().out.strip() == "1"
55
55
 
56
56
 
57
+ def test_episode_count_and_list_use_the_training_date_window(
58
+ monkeypatch, capsys
59
+ ) -> None:
60
+ store = InMemoryStore()
61
+ monkeypatch.setattr(cli, "get_default_store", lambda: store)
62
+ for index, created_at in enumerate(
63
+ (
64
+ "2026-08-09T05:40:00+00:00",
65
+ "2026-08-09T06:07:25+00:00",
66
+ "2026-08-09T06:12:23+00:00",
67
+ "2026-08-09T06:13:10+00:00",
68
+ "2026-08-09T06:13:55+00:00",
69
+ "2026-08-09T06:14:37+00:00",
70
+ "2026-08-09T06:20:00+00:00",
71
+ )
72
+ ):
73
+ episode = _full_episode()
74
+ episode.id = f"episode-{index}"
75
+ episode.created_at = created_at
76
+ store.store_episode(episode)
77
+
78
+ window = [
79
+ "--task-id",
80
+ "chat",
81
+ "--start-date",
82
+ "2026-08-09T05:42:45.258Z",
83
+ "--end-date",
84
+ "2026-08-09T06:16:07.333Z",
85
+ ]
86
+ assert cli.main(["task-episodes-count", "agent-1", *window]) == 0
87
+ assert capsys.readouterr().out.strip() == "5"
88
+ assert cli.main(["task-episodes-list", "agent-1", *window]) == 0
89
+ listed = json.loads(capsys.readouterr().out)
90
+ assert len(listed) == 5
91
+ assert all(
92
+ "2026-08-09T05:42:45.258+00:00"
93
+ <= item["episode"]["created_at"]
94
+ <= "2026-08-09T06:16:07.333+00:00"
95
+ for item in listed
96
+ )
97
+
98
+
57
99
  def test_episode_inspection_includes_scores_and_final_reward(monkeypatch, capsys) -> None:
58
100
  store = InMemoryStore()
59
101
  episode = _full_episode()
@@ -317,4 +359,51 @@ def test_agent_training_uses_one_limit_and_preserves_task_policy_history(
317
359
 
318
360
  def test_episode_limit_is_capped_at_500() -> None:
319
361
  with pytest.raises(SystemExit):
320
- cli.main(["train", "--agent-id", "agent-1", "--limit", "501"])
362
+ cli.main(["train", "--agent-id", "agent-1", "--limit", "501"])
363
+
364
+
365
+ def test_train_enforces_minimum_selected_episode_count(
366
+ monkeypatch, capsys
367
+ ) -> None:
368
+ store = InMemoryStore()
369
+ monkeypatch.setattr(cli, "get_default_store", lambda: store)
370
+ policy = SoftmaxPolicy.from_actions(
371
+ [Action(id="respond")], agent_id="agent-1", task_id="chat"
372
+ )
373
+ store.store_policy(policy.snapshot())
374
+ for index in range(3):
375
+ episode = _full_episode()
376
+ episode.id = f"episode-{index}"
377
+ store.store_episode(episode)
378
+ store.store_reward(
379
+ Reward(
380
+ episode_id=episode.id,
381
+ agent_id=episode.agent_id,
382
+ source=RewardSource.AGGREGATE,
383
+ value=0.8,
384
+ )
385
+ )
386
+
387
+ result = cli.main(
388
+ [
389
+ "train",
390
+ "--agent-id",
391
+ "agent-1",
392
+ "--task-id",
393
+ "chat",
394
+ "--min-episodes",
395
+ "5",
396
+ "--skip-scoring",
397
+ ]
398
+ )
399
+ output = json.loads(capsys.readouterr().out)
400
+
401
+ assert result == 2
402
+ assert output["runs"] == []
403
+ assert output["skipped"] == [
404
+ {
405
+ "task_id": "chat",
406
+ "reason": "selected batch has 3 episodes; minimum is 5",
407
+ }
408
+ ]
409
+ assert store.get_active_policy("agent-1", "chat").version == 0
@@ -135,6 +135,9 @@ def test_query_filters_and_limit(tmp_path: Path) -> None:
135
135
  # Filter by window
136
136
  windowed = store.query_episodes("dq", start_date="2024-05-01", end_date="2024-07-01")
137
137
  assert len(windowed) == 1
138
+ assert store.count_episodes(
139
+ "dq", start_date="2024-05-01", end_date="2024-07-01"
140
+ ) == 1
138
141
  # Limit
139
142
  assert len(store.query_episodes("dq", limit=1)) == 1
140
143
 
@@ -1,15 +0,0 @@
1
- # agent-learning
2
-
3
- Native reinforcement learning SDK for AI agents. An in-process learner optimizes a small, interpretable policy over discrete agent choices (understand intent and complete task by choosing the right outcome).
4
-
5
- ## How it works
6
-
7
- The SDK improves agents without LLM weight fine-tuning. There are no GPU fine-tune jobs and no opaque update cycles — just three pieces that run in your existing Python process:
8
-
9
- 1. The **policy** is a softmax distribution over `N` discrete actions (e.g., "take action A", "take action B", "take action C"). It lives in Python and updates in milliseconds.
10
-
11
- 2. Each episode is **evaluated** on-device by three stdlib scorers for intent resolution, task adherence, and task completion. Their scores are combined into a single scalar reward with no scoring endpoint or environment variables required. Azure AI evaluators remain available as an opt-in.
12
-
13
- 3. A **REINFORCE-with-baseline** learner updates the policy logits directly from logged episodes. Updates are tiny gradient steps that run on local compute and persist through a pluggable store — in-memory or local files by default, with Azure Cosmos DB optional.
14
-
15
- Every episode, reward, run, and deployment is captured by the configured store — in-memory or local files by default, or Azure Cosmos DB — giving you a complete lineage and audit trail of how the policy evolved over time.
File without changes
File without changes