item-extractor 1.0.0__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.
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.3
2
+ Name: item-extractor
3
+ Version: 1.0.0
4
+ Summary: Extract todos, tasks, reminders, and events from natural language
5
+ Author: UnknwnDev
6
+ Author-email: UnknwnDev <0unknwn.dev@gmail.com>
7
+ License: MIT
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Dist: dateparser>=1.4.3
12
+ Requires-Dist: spacy>=3.8.16
13
+ Requires-Dist: ipykernel>=7.3.0 ; extra == 'notebook'
14
+ Requires-Dist: pandas>=3.0.6 ; extra == 'notebook'
15
+ Requires-Dist: scikit-learn>=1.9.1 ; extra == 'notebook'
16
+ Requires-Dist: pytest>=9.1.1 ; extra == 'notebook'
17
+ Requires-Python: >=3.12
18
+ Provides-Extra: notebook
19
+ Description-Content-Type: text/markdown
20
+
21
+ # Item Extractor
22
+
23
+ [![PyPI version](https://shields.io)](https://pypi.org)
24
+ [![Python versions](https://shields.io)](https://pypi.org)
25
+
26
+ lightweight feature extraction that turns natural-language into structured JSON of type tasks, events, todos, etc...
27
+
28
+ ## Installation
29
+
30
+ Choose the installation method that fits your use case:
31
+
32
+ ### 1. Standard Installation (Production / Inference)
33
+ If you only need to run existing agents, install the core package. This keeps the installation lightweight and bundles the standard **spaCy `en_core_web_md` model** automatically:
34
+ ```bash
35
+ uv add item_extractor
36
+ # or via pip
37
+ pip install item_extractor
38
+ ```
39
+
40
+ ### 2. Notebook Installation (Development / Intent Classification Custom Agent Training)
41
+ If you want to use our interactive Jupyter Notebooks to design, benchmark, and train your own custom agent models, install the package with the `notebook` extra dependencies:
42
+ ```bash
43
+ uv add "item_extractor[notebook]"
44
+ # or via pip
45
+ pip install "item_extractor[notebook]"
46
+ ```
47
+
48
+ ---
49
+
50
+ ## 🚀 Quick Start (Core Library)
51
+
52
+ Use the built-in components to run an agent directly in your Python application:
53
+
54
+ ```python
55
+ from item_extractor import extract
56
+
57
+ items = extract("Organize the garage")
58
+ print(response) # Returns Todo(title='Organize the garage', description='', type=<ItemType.TODO: 'todo'>, completed=False)
59
+ ```
60
+
61
+ ---
62
+
63
+ ## 🧠 Creating Custom Intent Classification Agent Model
64
+
65
+ If you installed the package with the `[notebook]` extras, you can create and fine-tune your own agent architectures.
66
+
67
+ 1. Clone this repository to access the starter templates:
68
+ ```bash
69
+ git clone https://github.com/UnknwnDev/item-extractor.git
70
+ cd item_extractor
71
+ ```
72
+ 2. Open `notebooks/create_custom_agent.ipynb` and follow the step-by-step guide to train your agent using spaCy embeddings, customize decision thresholds, and evaluate agent trajectories.
73
+
74
+
75
+
76
+ ## 🛠️ Contribution & Local Setup
77
+
78
+ For developers looking to contribute to the codebase:
79
+
80
+ ```bash
81
+ # Clone and sync all environment dependencies including notebook extras
82
+ git clone https://github.com/UnknwnDev/item-extractor.git
83
+ cd item_extractor
84
+ uv sync --extra notebook
85
+
86
+ # Run the test suite
87
+ uv run pytest
88
+ ```
89
+
90
+ ## 📄 License
91
+
92
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,72 @@
1
+ # Item Extractor
2
+
3
+ [![PyPI version](https://shields.io)](https://pypi.org)
4
+ [![Python versions](https://shields.io)](https://pypi.org)
5
+
6
+ lightweight feature extraction that turns natural-language into structured JSON of type tasks, events, todos, etc...
7
+
8
+ ## Installation
9
+
10
+ Choose the installation method that fits your use case:
11
+
12
+ ### 1. Standard Installation (Production / Inference)
13
+ If you only need to run existing agents, install the core package. This keeps the installation lightweight and bundles the standard **spaCy `en_core_web_md` model** automatically:
14
+ ```bash
15
+ uv add item_extractor
16
+ # or via pip
17
+ pip install item_extractor
18
+ ```
19
+
20
+ ### 2. Notebook Installation (Development / Intent Classification Custom Agent Training)
21
+ If you want to use our interactive Jupyter Notebooks to design, benchmark, and train your own custom agent models, install the package with the `notebook` extra dependencies:
22
+ ```bash
23
+ uv add "item_extractor[notebook]"
24
+ # or via pip
25
+ pip install "item_extractor[notebook]"
26
+ ```
27
+
28
+ ---
29
+
30
+ ## 🚀 Quick Start (Core Library)
31
+
32
+ Use the built-in components to run an agent directly in your Python application:
33
+
34
+ ```python
35
+ from item_extractor import extract
36
+
37
+ items = extract("Organize the garage")
38
+ print(response) # Returns Todo(title='Organize the garage', description='', type=<ItemType.TODO: 'todo'>, completed=False)
39
+ ```
40
+
41
+ ---
42
+
43
+ ## 🧠 Creating Custom Intent Classification Agent Model
44
+
45
+ If you installed the package with the `[notebook]` extras, you can create and fine-tune your own agent architectures.
46
+
47
+ 1. Clone this repository to access the starter templates:
48
+ ```bash
49
+ git clone https://github.com/UnknwnDev/item-extractor.git
50
+ cd item_extractor
51
+ ```
52
+ 2. Open `notebooks/create_custom_agent.ipynb` and follow the step-by-step guide to train your agent using spaCy embeddings, customize decision thresholds, and evaluate agent trajectories.
53
+
54
+
55
+
56
+ ## 🛠️ Contribution & Local Setup
57
+
58
+ For developers looking to contribute to the codebase:
59
+
60
+ ```bash
61
+ # Clone and sync all environment dependencies including notebook extras
62
+ git clone https://github.com/UnknwnDev/item-extractor.git
63
+ cd item_extractor
64
+ uv sync --extra notebook
65
+
66
+ # Run the test suite
67
+ uv run pytest
68
+ ```
69
+
70
+ ## 📄 License
71
+
72
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,51 @@
1
+ [project]
2
+ name = "item-extractor"
3
+ version = "1.0.0"
4
+ description = "Extract todos, tasks, reminders, and events from natural language"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ classifiers = [
8
+ "Programming Language :: Python :: 3",
9
+ "License :: OSI Approved :: MIT License",
10
+ "Operating System :: OS Independent",
11
+ ]
12
+ dependencies = [
13
+ "dateparser>=1.4.3",
14
+ "spacy>=3.8.16",
15
+ ]
16
+
17
+ [[project.authors]]
18
+ name = "UnknwnDev"
19
+ email = "0unknwn.dev@gmail.com"
20
+
21
+ [project.license]
22
+ text = "MIT"
23
+
24
+ [project.optional-dependencies]
25
+ notebook = [
26
+ "ipykernel>=7.3.0",
27
+ "pandas>=3.0.6",
28
+ "scikit-learn>=1.9.1",
29
+ "pytest>=9.1.1",
30
+ ]
31
+
32
+ [tool.uv.sources.en_core_web_md]
33
+ url = "https://github.com"
34
+
35
+ [tool.hatch.build.targets.wheel]
36
+ exclude = [
37
+ "*.ipynb",
38
+ "notebooks/",
39
+ "tests/",
40
+ ]
41
+
42
+ [tool.hatch.build.targets.sdist]
43
+ exclude = [
44
+ "*.ipynb",
45
+ "notebooks/",
46
+ "tests/",
47
+ ]
48
+
49
+ [build-system]
50
+ requires = ["uv_build>=0.12.9,<0.13.0"]
51
+ build-backend = "uv_build"
@@ -0,0 +1,51 @@
1
+ [project]
2
+ name = "item-extractor"
3
+ version = "1.0.0"
4
+ description = "Extract todos, tasks, reminders, and events from natural language"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "UnknwnDev", email = "0unknwn.dev@gmail.com" }
8
+ ]
9
+ requires-python = ">=3.12"
10
+ license = { text = "MIT" }
11
+ classifiers = [
12
+ "Programming Language :: Python :: 3",
13
+ "License :: OSI Approved :: MIT License",
14
+ "Operating System :: OS Independent",
15
+ ]
16
+
17
+ dependencies = [
18
+ "dateparser>=1.4.3",
19
+ "spacy>=3.8.16",
20
+ ]
21
+
22
+ [tool.uv.sources]
23
+ en_core_web_md = { url = "https://github.com" }
24
+
25
+ [project.optional-dependencies]
26
+ notebook = [
27
+ "ipykernel>=7.3.0",
28
+ "pandas>=3.0.6",
29
+ "scikit-learn>=1.9.1",
30
+ "pytest>=9.1.1",
31
+ ]
32
+
33
+ [tool.hatch.build.targets.wheel]
34
+ # Exclude notebooks from the final .whl package
35
+ exclude = [
36
+ "*.ipynb",
37
+ "notebooks/",
38
+ "tests/",
39
+ ]
40
+
41
+ [tool.hatch.build.targets.sdist]
42
+ # Exclude notebooks from the source distribution archive (.tar.gz)
43
+ exclude = [
44
+ "*.ipynb",
45
+ "notebooks/",
46
+ "tests/",
47
+ ]
48
+
49
+ [build-system]
50
+ requires = ["uv_build>=0.12.9,<0.13.0"]
51
+ build-backend = "uv_build"
@@ -0,0 +1,28 @@
1
+ from .extractor import Extractor
2
+ from .models import (
3
+ Event,
4
+ ExtractedItem,
5
+ ItemType,
6
+ Reminder,
7
+ Task,
8
+ Todo,
9
+ )
10
+
11
+ # Create a default instance on module load
12
+ _default_extractor = Extractor()
13
+
14
+
15
+ def extract(text: str) -> list[ExtractedItem]:
16
+ return _default_extractor.extract(text)
17
+
18
+
19
+ __all__ = [
20
+ "extract",
21
+ "Extractor",
22
+ "ExtractedItem",
23
+ "ItemType",
24
+ "Todo",
25
+ "Task",
26
+ "Reminder",
27
+ "Event",
28
+ ]
@@ -0,0 +1,143 @@
1
+ import dateparser
2
+ import joblib
3
+ import spacy
4
+
5
+ from datetime import datetime, timedelta
6
+ from spacy.matcher import Matcher
7
+
8
+ from .models import *
9
+
10
+
11
+ class Extractor:
12
+ def __init__(self, model="en_core_web_md", timezone="PST") -> None:
13
+ self.classifier = joblib.load("model/intent_classifier.joblib")
14
+ self.nlp = spacy.load(model)
15
+ self.matcher = Matcher(self.nlp.vocab)
16
+ self.timezone = timezone
17
+
18
+ todo_pattern = [
19
+ {"POS": "VERB"},
20
+ {"POS": "DET", "OP": "?"},
21
+ {"POS": "ADJ", "OP": "*"},
22
+ {"POS": "NOUN", "OP": "*"},
23
+ {"POS": "NOUN"},
24
+ ]
25
+
26
+ task_pattern = [
27
+ {"POS": "VERB"},
28
+ {"POS": {"IN": ["DET", "ADJ", "NOUN", "PROPN", "NUM"]}, "OP": "*"},
29
+ {"POS": "NOUN"},
30
+ ]
31
+
32
+ reminder_pattern = [
33
+ {"LOWER": "remind"},
34
+ {"POS": {"IN": ["PRON", "PROPN"]}},
35
+ {"LOWER": "to"},
36
+ {"POS": "VERB"},
37
+ {"OP": "{1,4}"}, # Captures the next 1-4 words
38
+ ]
39
+
40
+ event_pattern = [
41
+ {"POS": {"IN": ["NOUN", "ADJ", "PROPN"]}, "OP": "*"},
42
+ {"LEMMA": {"IN": ["appointment", "meeting", "lunch", "event", "session"]}},
43
+ ]
44
+
45
+ self.matcher.add("TODO", [todo_pattern])
46
+ self.matcher.add("TASK", [task_pattern])
47
+ self.matcher.add("REMINDER", [reminder_pattern])
48
+ self.matcher.add("EVENT", [event_pattern])
49
+
50
+ def parse_command(self, text: str):
51
+ """Extracts item title, datetime, and location from given text for item creation.
52
+
53
+ Args:
54
+ text (str): user input
55
+
56
+ Returns:
57
+ dict: dictionary of title, datetime, and location
58
+ """
59
+ doc = self.nlp(text)
60
+ matches = self.matcher(doc)
61
+
62
+ if not matches:
63
+ return {"title": "Could not extract a pattern."}
64
+
65
+ longest_match = None
66
+ max_length = 0
67
+
68
+ for match_id, start, end in matches:
69
+ match_length = end - start
70
+ if match_length > max_length:
71
+ max_length = match_length
72
+ longest_match = (match_id, start, end)
73
+
74
+ match_id, start, end = longest_match # type: ignore
75
+ intent_label = self.nlp.vocab.strings[match_id]
76
+
77
+ if intent_label == "REMINDER":
78
+ match_span = doc[start:end]
79
+ to_index = start + 2
80
+
81
+ for token in match_span:
82
+ if token.lower_ == "to":
83
+ to_index = token.i
84
+ break
85
+
86
+ extracted_phrase = doc[to_index + 1 : end].text
87
+ else:
88
+ extracted_phrase = doc[start:end].text
89
+
90
+ date: str = ""
91
+ time: str = ""
92
+ location: str | None = None
93
+
94
+ for ent in doc.ents:
95
+ if ent.label_ == "DATE":
96
+ date = ent.text
97
+ if ent.label_ == "TIME":
98
+ time = ent.text
99
+ if ent.label_ in ["GPE", "LOC"]:
100
+ location = ent.text
101
+
102
+ date_time: datetime | None = dateparser.parse(
103
+ date + time, settings={"TIMEZONE": self.timezone}
104
+ )
105
+
106
+ data = {"title": extracted_phrase, "datetime": date_time, "location": location}
107
+ return data
108
+
109
+ def extract(self, text: str) -> ExtractedItem:
110
+ """Extract todo, task, reminder, and event from natural language.
111
+
112
+ Args:
113
+ text (str): user input
114
+
115
+ Returns:
116
+ ExtractedItem: Object of Todo | Task | Reminder | Event | None
117
+ """
118
+ # Predict the category without any hardcoded rules
119
+ prediction = self.classifier.predict([text])[0]
120
+
121
+ item = None
122
+
123
+ data = self.parse_command(text)
124
+
125
+ print(data)
126
+ if prediction == "todo":
127
+ item = Todo(title=data["title"])
128
+ elif prediction == "reminder":
129
+ item = Reminder(title=data["title"])
130
+ elif prediction == "event":
131
+ item = Event(
132
+ title=data["title"],
133
+ start_at=data["datetime"], # type: ignore
134
+ end_at=data["datetime"] + timedelta(hours=1), # type: ignore
135
+ location=data["location"]
136
+ )
137
+
138
+ elif prediction == "task":
139
+ item = Task(title=data["title"], due_at=data["datetime"]) # type: ignore
140
+
141
+ print(item, prediction)
142
+
143
+ return item
@@ -0,0 +1,52 @@
1
+ from datetime import datetime
2
+ from enum import StrEnum
3
+ from typing import Annotated, Literal
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class ItemType(StrEnum):
9
+ TODO = "todo"
10
+ TASK = "task"
11
+ REMINDER = "reminder"
12
+ EVENT = "event"
13
+
14
+
15
+ class BaseItem(BaseModel):
16
+ title: str
17
+ description: str = ""
18
+
19
+
20
+ class Todo(BaseItem):
21
+ type: Literal[ItemType.TODO] = ItemType.TODO
22
+
23
+ completed: bool = False
24
+
25
+
26
+ class Task(BaseItem):
27
+ type: Literal[ItemType.TASK] = ItemType.TASK
28
+
29
+ due_at: datetime | None = None
30
+ priority: int | None = Field(default=None, ge=1, le=5)
31
+
32
+
33
+ class Reminder(BaseItem):
34
+ type: Literal[ItemType.REMINDER] = ItemType.REMINDER
35
+
36
+ remind_at: datetime | None = None
37
+ recurrence: str | None = None
38
+
39
+
40
+ class Event(BaseItem):
41
+ type: Literal[ItemType.EVENT] = ItemType.EVENT
42
+
43
+ start_at: datetime
44
+ end_at: datetime | None = None
45
+ location: str | None = None
46
+ attendees: list[str] = Field(default_factory=list)
47
+
48
+
49
+ ExtractedItem = Annotated[
50
+ Todo|Task|Reminder|Event|None,
51
+ Field(discriminator="type"),
52
+ ]