Skip to content

Commit 7a14014

Browse files
committed
Background thoughts.
1 parent bb38ecf commit 7a14014

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1587
-1059
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from types import SimpleNamespace
2+
3+
from .coordinator_next_action import get_coordinator_next_action_suggestion
4+
from .detect_audience_and_takeaways import detect_audience_and_takeaways
5+
from .team_welcome import generate_team_welcome_message
6+
from .update_digest import update_digest
7+
8+
agentic = SimpleNamespace(
9+
get_coordinator_next_action_suggestion=get_coordinator_next_action_suggestion,
10+
detect_audience_and_takeaways=detect_audience_and_takeaways,
11+
generate_team_welcome_message=generate_team_welcome_message,
12+
update_digest=update_digest,
13+
)
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"""
2+
Coordinator support and helper functions for Knowledge Transfer Assistant.
3+
4+
Provides next action suggestions and other coordinator utilities.
5+
"""
6+
7+
from semantic_workbench_assistant.assistant_app import ConversationContext
8+
9+
from assistant.data import RequestStatus
10+
from assistant.domain import ShareManager, TransferManager
11+
from assistant.logging import logger
12+
13+
14+
async def get_coordinator_next_action_suggestion(
15+
context: ConversationContext,
16+
) -> str | None:
17+
"""
18+
Generate next action suggestions for the coordinator based on the knowledge transfer state.
19+
20+
This output is passed to the assistant and helps guide the conversation toward completing or improving
21+
the knowledge share in a helpful, structured way.
22+
23+
Returns:
24+
A user-facing suggestion string, or None if no suggestion is needed.
25+
"""
26+
try:
27+
share_id = await ShareManager.get_share_id(context)
28+
if not share_id:
29+
logger.warning("No share ID found for this conversation")
30+
return None
31+
32+
share = await ShareManager.get_share(context)
33+
if not share:
34+
return None
35+
36+
brief = share.brief
37+
requests = share.requests
38+
active_requests = [r for r in requests if r.status == RequestStatus.NEW]
39+
40+
# 1. Unresolved requests come first
41+
if active_requests:
42+
request = active_requests[0]
43+
return (
44+
f"There are {len(active_requests)} unanswered questions from team members. "
45+
f'One of them is: "{request.title}" Let\'s work on answering it.'
46+
)
47+
48+
# 2. Audience not yet defined
49+
if not share.audience:
50+
return (
51+
"Let's start by defining who your audience is. Who is this knowledge for, and what's their background?"
52+
)
53+
54+
# 3. Knowledge not yet organized
55+
# if not share.knowledge_organized:
56+
# return (
57+
# "Next, let's organize your knowledge. Upload any relevant files or describe the knowledge "
58+
# "you want to transfer. When you're ready, I can mark the knowledge as organized."
59+
# )
60+
61+
# 4. Brief not yet written
62+
if not brief:
63+
return (
64+
"Your knowledge share-out needs a short introduction that will orient your team. "
65+
"Let's write a knowledge brief next. The knowledge brief helps your team understand "
66+
"the purpose of this knowledge transfer and will be visible to all team members in their side panel."
67+
)
68+
69+
# 5. If intended to have outcomes but none defined yet
70+
if share.is_intended_to_accomplish_outcomes and not share.learning_objectives:
71+
return (
72+
"Would you like your team to achieve any specific outcomes? If so, let's define some learning objectives. " # noqa: E501
73+
"If not, you can mark this share-out as 'exploratory' instead."
74+
)
75+
76+
# 6. Objectives exist, but missing outcomes
77+
if share.is_intended_to_accomplish_outcomes:
78+
incomplete_objectives = [obj for obj in share.learning_objectives if not obj.learning_outcomes]
79+
if incomplete_objectives:
80+
name = incomplete_objectives[0].name
81+
return (
82+
f"The learning objective '{name}' doesn't have any outcomes yet. "
83+
f"Let's define what your team should accomplish to meet it."
84+
)
85+
86+
# 7. Ready for transfer but not yet shared
87+
if TransferManager.is_ready_for_transfer(share) and not TransferManager.is_actively_sharing(share):
88+
return (
89+
"Your knowledge is ready to share. Would you like to create a message and generate the invitation link?"
90+
)
91+
92+
# 8. Actively sharing - monitor and support ongoing transfer
93+
if TransferManager.is_actively_sharing(share):
94+
if share.is_intended_to_accomplish_outcomes and not TransferManager._is_transfer_complete(share):
95+
team_count = len(share.team_conversations)
96+
return (
97+
f"Great! Your knowledge is being shared with {team_count} team member"
98+
f"{'s' if team_count != 1 else ''}. You can continue improving the knowledge share or "
99+
f"respond to information requests as they come in."
100+
)
101+
else:
102+
return (
103+
"Your knowledge transfer is in progress. You can continue improving the knowledge share or "
104+
"respond to information requests as they come in."
105+
)
106+
107+
# 9. Default: General support
108+
return (
109+
"Your knowledge share is available. You can continue improving it or "
110+
"respond to new information requests as they come in."
111+
)
112+
113+
except Exception as e:
114+
logger.exception(f"Error generating next action suggestion: {e}")
115+
return None

assistants/knowledge-transfer-assistant/assistant/agentic/coordinator_support.py

Lines changed: 0 additions & 121 deletions
This file was deleted.
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
"""
2+
Analysis and detection functions for the knowledge transfer assistant.
3+
4+
This module contains functions for analyzing messages and knowledge transfer content
5+
to detect specific conditions, such as information request needs.
6+
"""
7+
8+
from typing import Any
9+
10+
import openai_client
11+
from assistant_extensions.attachments import AttachmentsExtension
12+
from pydantic import BaseModel
13+
from semantic_workbench_assistant.assistant_app import ConversationContext
14+
15+
from assistant.config import assistant_config
16+
from assistant.data import InspectorTab
17+
from assistant.domain.share_manager import ShareManager
18+
from assistant.domain.thoughts_manager import ThoughtsManager
19+
from assistant.logging import logger
20+
from assistant.notifications import Notifications
21+
from assistant.prompt_utils import (
22+
ContextSection,
23+
ContextStrategy,
24+
Instructions,
25+
Prompt,
26+
add_context_to_prompt,
27+
)
28+
from assistant.utils import load_text_include
29+
30+
31+
async def detect_audience_and_takeaways(
32+
context: ConversationContext, attachments_extension: AttachmentsExtension
33+
) -> None:
34+
debug: dict[str, Any] = {
35+
"context": context,
36+
}
37+
38+
config = await assistant_config.get(context.assistant)
39+
40+
# Set up prompt instructions.
41+
audience_instructions = load_text_include("background_thought_audience.md")
42+
instructions = Instructions(audience_instructions)
43+
prompt = Prompt(
44+
role=config.prompt_config.coordinator_role,
45+
instructions=instructions,
46+
context_strategy=ContextStrategy.MULTI,
47+
)
48+
49+
# Add prompt context.
50+
role = await ShareManager.get_conversation_role(context)
51+
await add_context_to_prompt(
52+
prompt,
53+
context=context,
54+
role=role,
55+
model=config.request_config.openai_model,
56+
token_limit=config.request_config.max_tokens,
57+
attachments_extension=attachments_extension,
58+
attachments_config=config.attachments_config,
59+
attachments_in_system_message=True,
60+
include=[
61+
# ContextSection.KNOWLEDGE_INFO,
62+
# ContextSection.KNOWLEDGE_BRIEF,
63+
ContextSection.TARGET_AUDIENCE,
64+
ContextSection.LEARNING_OBJECTIVES,
65+
# ContextSection.KNOWLEDGE_DIGEST,
66+
# ContextSection.INFORMATION_REQUESTS,
67+
# ContextSection.SUGGESTED_NEXT_ACTIONS,
68+
ContextSection.COORDINATOR_CONVERSATION,
69+
ContextSection.ATTACHMENTS,
70+
ContextSection.ASSISTANT_THOUGHTS,
71+
],
72+
)
73+
74+
class Output(BaseModel):
75+
"""Output class to hold the generated thoughts."""
76+
77+
thoughts: list[
78+
str
79+
] # Generated thoughts about the audience and takeaways. One thought per item. If there are no thoughts, this will be an empty list. #noqa: E501
80+
81+
# Chat completion
82+
async with openai_client.create_client(config.service_config) as client:
83+
try:
84+
completion_args = {
85+
"messages": prompt.messages(),
86+
"model": config.request_config.openai_model,
87+
"max_tokens": 500,
88+
"temperature": 0.8,
89+
"response_format": Output,
90+
}
91+
debug["completion_args"] = openai_client.make_completion_args_serializable(completion_args)
92+
93+
# LLM call
94+
response = await client.beta.chat.completions.parse(
95+
**completion_args,
96+
)
97+
openai_client.validate_completion(response)
98+
debug["completion_response"] = openai_client.add_serializable_data(response.model_dump())
99+
100+
# Response
101+
if response and response.choices and response.choices[0].message.parsed:
102+
output: Output = response.choices[0].message.parsed
103+
if output.thoughts:
104+
await ThoughtsManager.add_assistant_thoughts(context, output.thoughts)
105+
await Notifications.notify_state_update(
106+
context,
107+
[InspectorTab.BRIEF],
108+
)
109+
else:
110+
logger.warning("Empty response from LLM for welcome message generation")
111+
112+
except Exception as e:
113+
logger.exception(f"Failed to make OpenIA call: {e}")
114+
debug["error"] = str(e)
115+
116+
logger.debug(f"{__name__}: {debug}")

assistants/knowledge-transfer-assistant/assistant/agentic/analysis.py renamed to assistants/knowledge-transfer-assistant/assistant/agentic/detect_information_requests.py

File renamed without changes.

0 commit comments

Comments
 (0)