Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c147d8be78 | |||
| 9ffaa18efe |
11
HISTORY.md
11
HISTORY.md
@@ -5,10 +5,21 @@ Changelog
|
|||||||
(unreleased)
|
(unreleased)
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
Fix
|
||||||
|
~~~
|
||||||
|
- Project association improvements, refs NOISSUE. [Simon Diesenreiter]
|
||||||
|
|
||||||
|
|
||||||
|
0.9.10 (2026-04-11)
|
||||||
|
-------------------
|
||||||
|
|
||||||
Fix
|
Fix
|
||||||
~~~
|
~~~
|
||||||
- More git integration fixes, refs NOISSUE. [Simon Diesenreiter]
|
- More git integration fixes, refs NOISSUE. [Simon Diesenreiter]
|
||||||
|
|
||||||
|
Other
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
|
||||||
0.9.9 (2026-04-11)
|
0.9.9 (2026-04-11)
|
||||||
------------------
|
------------------
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
0.9.10
|
0.9.11
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ class AgentOrchestrator:
|
|||||||
features=self.features,
|
features=self.features,
|
||||||
tech_stack=self.tech_stack,
|
tech_stack=self.tech_stack,
|
||||||
actor_name=self.prompt_actor,
|
actor_name=self.prompt_actor,
|
||||||
|
source=self.prompt_actor,
|
||||||
related_issue={'number': self.related_issue_number} if self.related_issue_number is not None else None,
|
related_issue={'number': self.related_issue_number} if self.related_issue_number is not None else None,
|
||||||
source_context=self.prompt_source_context,
|
source_context=self.prompt_source_context,
|
||||||
routing=self.prompt_routing,
|
routing=self.prompt_routing,
|
||||||
|
|||||||
@@ -31,6 +31,11 @@ class RequestInterpreter:
|
|||||||
PLACEHOLDER_PROJECT_NAME_WORDS = {
|
PLACEHOLDER_PROJECT_NAME_WORDS = {
|
||||||
'generated project', 'new project', 'project', 'temporary name', 'temp name', 'placeholder', 'untitled project',
|
'generated project', 'new project', 'project', 'temporary name', 'temp name', 'placeholder', 'untitled project',
|
||||||
}
|
}
|
||||||
|
ROUTING_STOPWORDS = REPO_NOISE_WORDS | GENERIC_PROJECT_NAME_WORDS | {
|
||||||
|
'about', 'after', 'again', 'appropriate', 'before', 'best', 'details', 'follow', 'following', 'implement',
|
||||||
|
'integration', 'instance', 'instances', 'later', 'make', 'now', 'primary', 'primarily', 'probably',
|
||||||
|
'remember', 'specific', 'suite', 'tearing', 'testing', 'through', 'used', 'using', 'workflow', 'workflows',
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, ollama_url: str | None = None, model: str | None = None):
|
def __init__(self, ollama_url: str | None = None, model: str | None = None):
|
||||||
self.ollama_url = (ollama_url or settings.ollama_url).rstrip('/')
|
self.ollama_url = (ollama_url or settings.ollama_url).rstrip('/')
|
||||||
@@ -468,6 +473,7 @@ class RequestInterpreter:
|
|||||||
projects = context.get('projects', [])
|
projects = context.get('projects', [])
|
||||||
last_project_id = recent_history[0].get('project_id') if recent_history else None
|
last_project_id = recent_history[0].get('project_id') if recent_history else None
|
||||||
last_issue = ((recent_history[0].get('related_issue') or {}).get('number') if recent_history else None)
|
last_issue = ((recent_history[0].get('related_issue') or {}).get('number') if recent_history else None)
|
||||||
|
last_project = next((project for project in projects if project.get('project_id') == last_project_id), None) if last_project_id else None
|
||||||
|
|
||||||
matched_project = None
|
matched_project = None
|
||||||
for project in projects:
|
for project in projects:
|
||||||
@@ -481,8 +487,24 @@ class RequestInterpreter:
|
|||||||
break
|
break
|
||||||
if matched_project is None and not explicit_new:
|
if matched_project is None and not explicit_new:
|
||||||
follow_up_tokens = ['also', 'continue', 'for this project', 'for that project', 'work on this', 'work on that', 'fix that', 'add this']
|
follow_up_tokens = ['also', 'continue', 'for this project', 'for that project', 'work on this', 'work on that', 'fix that', 'add this']
|
||||||
if any(token in lowered for token in follow_up_tokens) and last_project_id:
|
leading_follow_up = lowered.startswith(('also', 'now', 'continue', 'remember', 'then'))
|
||||||
matched_project = next((project for project in projects if project.get('project_id') == last_project_id), None)
|
recent_overlap = 0
|
||||||
|
if last_project is not None:
|
||||||
|
recent_prompt_text = recent_history[0].get('prompt_text') or ''
|
||||||
|
project_reference_text = ' '.join(
|
||||||
|
part for part in [
|
||||||
|
last_project.get('name') or '',
|
||||||
|
last_project.get('description') or '',
|
||||||
|
((last_project.get('repository') or {}).get('name') or ''),
|
||||||
|
]
|
||||||
|
if part
|
||||||
|
)
|
||||||
|
recent_overlap = len(
|
||||||
|
self._routing_tokens(prompt_text)
|
||||||
|
& (self._routing_tokens(recent_prompt_text) | self._routing_tokens(project_reference_text))
|
||||||
|
)
|
||||||
|
if last_project_id and (leading_follow_up or any(token in lowered for token in follow_up_tokens) or recent_overlap >= 2):
|
||||||
|
matched_project = last_project
|
||||||
issue_number = referenced_issue
|
issue_number = referenced_issue
|
||||||
if issue_number is None and any(token in lowered for token in ['that issue', 'this issue', 'the issue']) and last_issue is not None:
|
if issue_number is None and any(token in lowered for token in ['that issue', 'this issue', 'the issue']) and last_issue is not None:
|
||||||
issue_number = last_issue
|
issue_number = last_issue
|
||||||
@@ -497,6 +519,14 @@ class RequestInterpreter:
|
|||||||
'reasoning_summary': 'Heuristic routing from chat history and project names.',
|
'reasoning_summary': 'Heuristic routing from chat history and project names.',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _routing_tokens(self, text: str) -> set[str]:
|
||||||
|
"""Extract meaningful tokens for heuristic continuation matching."""
|
||||||
|
cleaned = re.sub(r'[^a-z0-9]+', ' ', (text or '').lower())
|
||||||
|
return {
|
||||||
|
token for token in cleaned.split()
|
||||||
|
if len(token) >= 4 and token not in self.ROUTING_STOPWORDS
|
||||||
|
}
|
||||||
|
|
||||||
def _extract_issue_number(self, prompt_text: str) -> int | None:
|
def _extract_issue_number(self, prompt_text: str) -> int | None:
|
||||||
match = re.search(r'(?:#|issue\s+)(\d+)', prompt_text, flags=re.IGNORECASE)
|
match = re.search(r'(?:#|issue\s+)(\d+)', prompt_text, flags=re.IGNORECASE)
|
||||||
return int(match.group(1)) if match else None
|
return int(match.group(1)) if match else None
|
||||||
Reference in New Issue
Block a user