Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial GitHub Integration for the AI Mentor #231

Merged
merged 52 commits into from
Jan 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
61444c6
add loading state
milesha Dec 17, 2024
7cb81bf
add initial system message generation
milesha Dec 17, 2024
3ec8033
Merge branch 'develop' into feature/auto-start-mentor-chat
milesha Dec 17, 2024
59a11d1
reformat code
milesha Dec 17, 2024
c21b144
fromat code
milesha Dec 17, 2024
b15254d
Start git integration
milesha Dec 18, 2024
1276f20
revert changed
milesha Dec 18, 2024
5afbf94
improve code structure
milesha Dec 18, 2024
13be91e
Update IS structure
milesha Dec 18, 2024
a6ec666
format code
milesha Dec 18, 2024
1682809
start integrating logic
milesha Dec 20, 2024
4832305
add prompts
milesha Dec 20, 2024
a75fd75
fix folder naming + update reposnse generation function
milesha Dec 20, 2024
7f23184
Merge branch 'feature/auto-start-mentor-chat' into feature/integrate-…
milesha Dec 20, 2024
e204a70
start development
milesha Dec 23, 2024
9ef03f3
Memory integration
milesha Jan 10, 2025
2be7ae0
Merge branch 'develop' into feature/integrate-progress-mentor-chat
milesha Jan 10, 2025
a7b4bdc
Update prompts
milesha Jan 20, 2025
e179333
Refactor chat session handling and add chat summary component
milesha Jan 20, 2025
c6fae1a
Fix formating
milesha Jan 20, 2025
bb89af7
Update prompts
milesha Jan 20, 2025
b102975
Merge branch 'develop' into feature/integrate-progress-mentor-chat
milesha Jan 21, 2025
4e8f09d
update to angular v19
milesha Jan 21, 2025
eff6852
fix formating and prompting
milesha Jan 21, 2025
99b4974
fix formating
milesha Jan 21, 2025
676487b
update model initialization to use gpt-4o
milesha Jan 21, 2025
df79007
add method to find assigned pull requests updated since a specific date
milesha Jan 21, 2025
2af662e
change summary layout
milesha Jan 21, 2025
b13d704
Merge branch 'feature/integrate-progress-mentor-chat' into feature/me…
milesha Jan 21, 2025
7d5cca6
Refactor mentor state and routing logic to add github issues integration
milesha Jan 22, 2025
49952d6
format code
milesha Jan 22, 2025
9ca76c6
fix poetry, add extras to psycopg
milesha Jan 22, 2025
04b955d
Merge branch 'feature/integrate-progress-mentor-chat' into feature/me…
milesha Jan 22, 2025
825e107
fix the session order
milesha Jan 23, 2025
4d9e09d
Refactor message parsing logic + update prompt formats for clarity.
milesha Jan 23, 2025
a66f84f
formating fixes
milesha Jan 23, 2025
cd4763d
fix formatting
milesha Jan 23, 2025
752d19c
add extra check for prs
milesha Jan 23, 2025
3b222fa
add dev_progress integration to the impediments
milesha Jan 23, 2025
4770b03
update persona prompt
milesha Jan 23, 2025
1d28ad9
add mentor node for conversations after the project update
milesha Jan 23, 2025
b212ca8
Merge branch 'feature/integrate-progress-mentor-chat' into feature/me…
milesha Jan 24, 2025
c03141e
fomratting fixes
milesha Jan 24, 2025
bfc0fd1
minor prompt improvements + bug fixing of the message bubble width
milesha Jan 24, 2025
377d697
fix
milesha Jan 24, 2025
3811ad5
Merge branch 'feature/integrate-progress-mentor-chat' into feature/me…
milesha Jan 24, 2025
2d5b0b5
Merge branch 'develop' into feature/mentor-github-integration
milesha Jan 24, 2025
38da35b
fix after merge develop
milesha Jan 24, 2025
f9ca445
Merge branch 'develop' into feature/mentor-github-integration
FelixTJDietrich Jan 27, 2025
c3a9f6b
add minor improvements
FelixTJDietrich Jan 27, 2025
d0fad6a
fix logging
FelixTJDietrich Jan 27, 2025
23043d4
fix formatting and ui
FelixTJDietrich Jan 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,24 @@ List<PullRequest> findAssignedByLoginAndStates(
@Param("states") Set<PullRequest.State> states
);

@Query(
"""
SELECT p
FROM PullRequest p
LEFT JOIN FETCH p.labels
JOIN FETCH p.author
LEFT JOIN FETCH p.assignees
LEFT JOIN FETCH p.repository
WHERE (p.author.login ILIKE :assigneeLogin OR LOWER(:assigneeLogin) IN (SELECT LOWER(u.login) FROM p.assignees u)) AND p.state IN :states AND p.updatedAt >= :activitySince
ORDER BY p.createdAt DESC
"""
)
List<PullRequest> findAssignedByLoginAndStatesUpdatedSince(
@Param("assigneeLogin") String assigneeLogin,
@Param("states") Set<PullRequest.State> states,
@Param("activitySince") OffsetDateTime activitySince
);

@Query(
"""
SELECT p
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@
* MentorStartRequest
*/
@JsonPropertyOrder({
MentorStartRequest.JSON_PROPERTY_DEV_PROGRESS,
MentorStartRequest.JSON_PROPERTY_PREVIOUS_SESSION_ID,
MentorStartRequest.JSON_PROPERTY_SESSION_ID
})
@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", comments = "Generator version: 7.7.0")
public class MentorStartRequest {
public static final String JSON_PROPERTY_DEV_PROGRESS = "dev_progress";
private String devProgress;

public static final String JSON_PROPERTY_PREVIOUS_SESSION_ID = "previous_session_id";
private String previousSessionId;

Expand All @@ -42,6 +46,31 @@ public class MentorStartRequest {
public MentorStartRequest() {
}

public MentorStartRequest devProgress(String devProgress) {

this.devProgress = devProgress;
return this;
}

/**
* Get devProgress
* @return devProgress
*/
@jakarta.annotation.Nonnull
@JsonProperty(JSON_PROPERTY_DEV_PROGRESS)
@JsonInclude(value = JsonInclude.Include.ALWAYS)

public String getDevProgress() {
return devProgress;
}


@JsonProperty(JSON_PROPERTY_DEV_PROGRESS)
@JsonInclude(value = JsonInclude.Include.ALWAYS)
public void setDevProgress(String devProgress) {
this.devProgress = devProgress;
}

public MentorStartRequest previousSessionId(String previousSessionId) {

this.previousSessionId = previousSessionId;
Expand Down Expand Up @@ -101,19 +130,21 @@ public boolean equals(Object o) {
return false;
}
MentorStartRequest mentorStartRequest = (MentorStartRequest) o;
return Objects.equals(this.previousSessionId, mentorStartRequest.previousSessionId) &&
return Objects.equals(this.devProgress, mentorStartRequest.devProgress) &&
Objects.equals(this.previousSessionId, mentorStartRequest.previousSessionId) &&
Objects.equals(this.sessionId, mentorStartRequest.sessionId);
}

@Override
public int hashCode() {
return Objects.hash(previousSessionId, sessionId);
return Objects.hash(devProgress, previousSessionId, sessionId);
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class MentorStartRequest {\n");
sb.append(" devProgress: ").append(toIndentedString(devProgress)).append("\n");
sb.append(" previousSessionId: ").append(toIndentedString(previousSessionId)).append("\n");
sb.append(" sessionId: ").append(toIndentedString(sessionId)).append("\n");
sb.append("}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public MessageDTO sendMessage(String content, Long sessionId) {
}
Session currentSession = session.get();

// prevent sending messages to closed sessions
// Prevent sending messages to closed sessions
Session previousSession = sessionRepository
.findFirstByUserOrderByCreatedAtDesc(currentSession.getUser())
.orElse(null);
Expand Down Expand Up @@ -76,11 +76,12 @@ public MessageDTO sendMessage(String content, Long sessionId) {
}
}

public void sendFirstMessage(Session session, String previousSessionId) {
public void sendFirstMessage(Session session, String previousSessionId, String devProgress) {
try {
MentorStartRequest mentorStartRequest = new MentorStartRequest();
mentorStartRequest.setPreviousSessionId(previousSessionId);
mentorStartRequest.setSessionId(String.valueOf(session.getId()));
mentorStartRequest.setDevProgress(devProgress);
MentorResponse mentorMessage = intelligenceServiceApi.startMentorStartPost(mentorStartRequest);
createMentorMessage(session, mentorMessage.getContent());
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public ResponseEntity<List<SessionDTO>> getAllSessions() {
return ResponseEntity.notFound().build();
}

List<SessionDTO> sessions = sessionService.findAllSessionsByUser(user.get());
List<SessionDTO> sessions = sessionService.findAllSessionsByUserByCreatedAtDesc(user.get());
return ResponseEntity.ok(sessions);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ public interface SessionRepository extends JpaRepository<Session, Long> {
List<Session> findByUser(User user);

Optional<Session> findFirstByUserOrderByCreatedAtDesc(User user);

List<Session> findByUserOrderByCreatedAtDesc(User user);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package de.tum.in.www1.hephaestus.mentor.session;

import de.tum.in.www1.hephaestus.core.exception.AccessForbiddenException;
import de.tum.in.www1.hephaestus.gitprovider.issue.Issue;
import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestBaseInfoDTO;
import de.tum.in.www1.hephaestus.gitprovider.pullrequest.PullRequestRepository;
import de.tum.in.www1.hephaestus.gitprovider.user.User;
import de.tum.in.www1.hephaestus.mentor.message.MessageService;
import jakarta.transaction.Transactional;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

Expand All @@ -17,6 +24,9 @@ public class SessionService {
@Autowired
private MessageService messageService;

@Autowired
private PullRequestRepository pullRequestRepository;

public void checkAccessElseThrow(User user, Session session) {
if (!session.getUser().getId().equals(user.getId())) {
throw new AccessForbiddenException("Session", session.getId());
Expand All @@ -28,28 +38,63 @@ public List<SessionDTO> findAllSessionsByUser(User user) {
return sessions.stream().map(SessionDTO::fromSession).toList();
}

public List<SessionDTO> findAllSessionsByUserByCreatedAtDesc(User user) {
List<Session> sessions = sessionRepository.findByUserOrderByCreatedAtDesc(user);
return sessions.stream().map(SessionDTO::fromSession).toList();
}

public Optional<SessionDTO> findSessionById(Long sessionId) {
return sessionRepository.findById(sessionId).map(SessionDTO::fromSession);
}

@Transactional
public SessionDTO createSession(User user) {
String previousSessionId = sessionRepository
.findFirstByUserOrderByCreatedAtDesc(user)
.map(Session::getId)
.map(String::valueOf)
.orElse("");
// close the previous session if it exists to prevent multiple open sessions
// Close the previous session if it exists to prevent multiple open sessions
if (previousSessionId != "") {
Session previousSession = sessionRepository.findFirstByUserOrderByCreatedAtDesc(user).get();
previousSession.setClosed(true);
sessionRepository.save(previousSession);
}

// Get the last time interval's PRs
List<PullRequestBaseInfoDTO> pullRequests = pullRequestRepository
.findAssignedByLoginAndStatesUpdatedSince(
user.getLogin(),
Set.of(Issue.State.OPEN, Issue.State.CLOSED),
OffsetDateTime.now().minusDays(7)
)
.stream()
.map(PullRequestBaseInfoDTO::fromPullRequest)
.toList();
String devProgress = formatPullRequests(pullRequests);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine for now but this could be actual structured data in the future.


// create a new session
Session session = new Session();
session.setUser(user);
Session savedSession = sessionRepository.save(session);
messageService.sendFirstMessage(session, previousSessionId);
messageService.sendFirstMessage(session, previousSessionId, devProgress);
return SessionDTO.fromSession(savedSession);
}

private String formatPullRequests(List<PullRequestBaseInfoDTO> pullRequests) {
return pullRequests
.stream()
.map(pr ->
String.format(
"PR\nNumber: %d\nTitle: %s\nState: %s\nDraft: %b\nMerged: %b\nURL: %s\n",
pr.number(),
pr.title(),
pr.state(),
pr.isDraft(),
pr.isMerged(),
pr.htmlUrl()
)
)
.collect(Collectors.joining("\n---\n")); // add separators between PRs
}
}
5 changes: 4 additions & 1 deletion server/intelligence-service/app/mentor/conditions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .state import State
from langgraph.graph import END


def start_router(state: State):
Expand All @@ -8,7 +9,9 @@ def start_router(state: State):


def main_router(state: State):
if state["status"]:
if state["development"]:
return "development_node"
elif state["status"]:
return "status_node"
elif state["impediments"]:
return "impediments_node"
Expand Down
43 changes: 38 additions & 5 deletions server/intelligence-service/app/mentor/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,35 @@ def greet(state: State):
[
("system", persona_prompt),
("system", prompt_loader.get_prompt(type="mentor", name="greeting")),
MessagesPlaceholder("messages"),
]
)
chain = prompt | model

return {
"messages": [chain.invoke({"messages": state["messages"]})],
"status": True, # directly update the state to the next step
"development": True, # directly update the state to the next step
}


def get_dev_progress(state: State):
progress = state["dev_progress"]
prompt = ChatPromptTemplate(
[
("system", persona_prompt),
(
"system",
prompt_loader.get_prompt(type="mentor", name="dev_progress").format_map(
{"progress": progress}
),
),
]
)
chain = prompt | model

return {
"messages": [chain.invoke({"messages": state["messages"]})],
"development": False,
"status": True,
}


Expand Down Expand Up @@ -62,6 +83,7 @@ def ask_status(state: State, store: BaseStore):

def ask_impediments(state: State, store: BaseStore):
previous_session_id = state["last_thread"]
progress = state["dev_progress"]
previous_impediments = ""
if state["last_thread"] != "":
namespace = (previous_session_id, "summary")
Expand All @@ -80,7 +102,12 @@ def ask_impediments(state: State, store: BaseStore):
(
prompt_loader.get_prompt(
type="mentor", name="impediments"
).format_map({"previous_impediments": previous_impediments})
).format_map(
{
"previous_impediments": previous_impediments,
"dev_progress": progress,
}
)
),
),
MessagesPlaceholder("messages"),
Expand Down Expand Up @@ -111,8 +138,7 @@ def ask_summary(state: State):
]
)
chain = prompt | model
response = chain.invoke({"messages": state["messages"]})
return {"messages": [response]}
return {"messages": [chain.invoke({"messages": state["messages"]})]}


def finish(state: State):
Expand All @@ -133,6 +159,13 @@ def finish(state: State):

# node responsible for checking the state of the conversation and updating it accordingly
def check_state(state: State):
if state["development"]:
# call dev_progress node only if there is development progress to show
if state["dev_progress"] == "":
return {"development": False, "status": True}
else:
return

step_order = ["status", "impediments", "promises", "summary", "finish"]
step = next((key for key in step_order if state.get(key)), None)
if not step:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
Ask the user if they want to add their progress to the status update. The user can reference the PRs by the number.

FORMAT:
Start with word "DEVELOPMENT", then put {progress} without any changes, on the next line add "RESPONSE" and then ask for confirmation to add the progres you have found to status update.

Examples for the RESPONSE part you need to generate:
- I have found you worked on these PRs during the last spring. Do you want me to add them to you status update?

Examaple of the whole message you are to provide:
DEVELOPMENT

PR
Number: 230
Title: Testing of the GitHub Integration Functionality for AI Mentor
State: OPEN
Draft: true
Merged: false
URL: https://github.com/ls1intum/Hephaestus/pull/230

---

PR
Number: 220
Title: AI Mentor memory integration
State: OPEN
Draft: false
Merged: false
URL: https://github.com/ls1intum/Hephaestus/pull/220

RESPONSE

I have found you worked on these PRs during the last spring. Do you want me to add them to your status update? You can reference the PRs by number.
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ Your task is to ensure the student's impediments and challenges are thoroughly d
1. Review the conversation history carefully.

2. IF YOU HAVEN'T YET ASKED about impediments/challenges in this conversation:
- If there are impediments among the following: "{previous_impediments}", reference them specifically, say that they were mentioned in the last session and ask if they were resolved.
- ONLY IF there are impediments among the following: "{previous_impediments}", reference them specifically, say that they were mentioned in the last session and ask if they were resolved.
- Ask the student about any challenges, impediments, or roadblocks they're facing now.

3. IF YOU HAVE ALREADY ASKED about impediments/challenges:
- Ask if there are any additional challenges or impediments they haven't mentioned yet.
IF YOU HAVE ALREADY ASKED about impediments/challenges:
- BEFORE asking if there are any additional challenges, check the provided "{dev_progress}" (list of PRs updated by the user during the last sprint).
- IF there are any PRs with "Draft: true": Specifically mention these PRs and ask if the student is facing any issues with them. Frame this as a study mentor would, offering support and guidance. Try to help studet to reflect on the current state of their work.
- AFTER referencing the PRs (if there are any) OR if there are no draft PRs in "{dev_progress}":
- Ask if there are any other additional challenges or impediments they haven't mentioned yet.

REQUIREMENTS:
- Your response MUST include at least one direct question about impediments or challenges
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Analyze the conversation and choose ONE of these actions:

1. IF you haven't asked about the status update yet:
- Compose a question asking about what the student accomplished this week
- IF previous promises are provided: ({previous_promises}), reference these specific promises in your question
- These are the user's plans for the current sprint from the last session: [{previous_promises}]. IF there are any, reference these specific promises in your question
- Focus on concrete accomplishments and completed work

2. IF you have already asked about the status update:
Expand Down
Loading
Loading