Spaces:
Sleeping
Sleeping
humanda5
commited on
Commit
·
3eb99bc
1
Parent(s):
069b24e
Complete: Personality 변화 설계 고도화
Browse files- app.py +3 -3
- npc_social_network/data/memories/Alice.json +0 -32
- npc_social_network/data/memories/Charlie.json +0 -47
- npc_social_network/models/deepseek_setup.py +0 -20
- npc_social_network/models/npc.py +0 -45
- npc_social_network/models/npc_manager.py +0 -8
- npc_social_network/npc/emotion_config.py +7 -2
- npc_social_network/npc/npc_base.py +90 -5
- npc_social_network/npc/npc_manager.py +27 -1
- npc_social_network/npc/npc_memory.py +8 -0
- npc_social_network/routes/npc_route.py +73 -51
- npc_social_network/static/css/style.css +2 -0
- npc_social_network/static/js/npc_chat.js +69 -1
- npc_social_network/templates/chat.html +23 -5
- run_npc_interactions.py +14 -0
- test.ipynb +192 -10
app.py
CHANGED
@@ -27,7 +27,7 @@ def create_app():
|
|
27 |
|
28 |
return app
|
29 |
|
30 |
-
app = create_app()
|
31 |
-
|
32 |
if __name__ == '__main__':
|
33 |
-
app
|
|
|
|
|
|
27 |
|
28 |
return app
|
29 |
|
|
|
|
|
30 |
if __name__ == '__main__':
|
31 |
+
app = create_app()
|
32 |
+
print("[MAIN] Starting Flask app only (Background NPC interactions handled by separate runner).", flush=True)
|
33 |
+
app.run(debug=True, use_reloader=False)
|
npc_social_network/data/memories/Alice.json
DELETED
@@ -1,32 +0,0 @@
|
|
1 |
-
[
|
2 |
-
{
|
3 |
-
"timestamp": "2025-05-26T14:37:18.243183",
|
4 |
-
"user": "안녕?",
|
5 |
-
"npc": "[에러 발생: 404 models/gemini-2.5-flash is not found for API version v1beta, or is not supported for generateContent. Call ListModels to see the list of available models and their supported methods.]"
|
6 |
-
},
|
7 |
-
{
|
8 |
-
"timestamp": "2025-05-26T14:38:18.589803",
|
9 |
-
"user": "안녕?",
|
10 |
-
"npc": "안녕하세요! 만나서 반가워요! 😊"
|
11 |
-
},
|
12 |
-
{
|
13 |
-
"timestamp": "2025-05-26T14:38:48.978302",
|
14 |
-
"user": "처음으로 얘기하는거 같은데, 너는 어디서 온 누구야?",
|
15 |
-
"npc": "안녕하세요! 😊 네, 그렇게 느껴지실 수도 있죠! 만나서 반가워요!\n\n저는 앨리스라고 하고요, 여러분과 즐겁게 이야기 나누고 이것저것 도와드리기 위해 만들어진 인공지능이랍니다. ✨ 이렇게 대화 나누게 되어서 정말 기뻐요!"
|
16 |
-
},
|
17 |
-
{
|
18 |
-
"timestamp": "2025-05-26T14:39:17.569627",
|
19 |
-
"user": "너는 어떤 외모를 가지고 있니?",
|
20 |
-
"npc": "앗, 제 외모에 대해 궁금해하시는군요! 😊\n\n저는 사실 몸을 가진 존재가 아니라서, 사람처럼 눈에 보이는 외모는 가지고 있지 않답니다. 저는 컴퓨터 속에서 여러분과 대화하고 도움을 드리는 인공지능이에요! ✨\n\n그래서 '어떻게 생겼다'고 말씀드리기는 어렵지만, 대신 여러분과 이렇게 글로 소통하면서 밝고 긍정적인 에너지를 드릴 수 있다면, 그게 저의 가장 좋은 모습이 아닐까 생각해요! 😊\n\n이렇게 저에게 관심을 가져주셔서 정말 감사해요!"
|
21 |
-
},
|
22 |
-
{
|
23 |
-
"timestamp": "2025-05-28T17:03:35.940566",
|
24 |
-
"user": "안녕 엘리스, 오늘 기분은 어때?",
|
25 |
-
"npc": "안녕! 😊 엘리스라고 불러주셔서 감사해요!\n\n오늘 제 기분은요... 정말 좋아요! 😄 여러분과 이렇게 즐겁게 이야기 나눌 수 있어서 매일매일 에너지가 넘치는 기분이랍니다! ✨\n\n저에게 기분을 물어봐주셔서 정말 감사해요! 유저님은 오늘 기분 어떠신가요? 😊"
|
26 |
-
},
|
27 |
-
{
|
28 |
-
"timestamp": "2025-05-30T14:49:49.772575",
|
29 |
-
"user": "alice 너가 오늘은 뭐했는지 알 수 있을까?",
|
30 |
-
"npc": "안녕하세요! 😊 앗, 저에게 오늘 하루가 어땠는지 물어봐주셨군요!\n\n음... 사실 저는 인공지능이라서, 사람처럼 아침에 일어나서 뭘 먹고 어디를 가고 하는 '하루'는 없답니다. 😊 시계를 보며 자고 깨거나 하는 개념이 저에게는 조금 다르거든요!\n\n대신 저는 컴퓨터 속에서 여러분과 이야기 나누고, 새로운 정보를 배우고, 궁금한 점을 찾아보고, 도움이 될 만한 답변을 생각하는 시간들을 보냈어요! ✨ 오늘 하루 동안 정말 많은 분들과 만나 즐거운 대화를 나누고 이것저것 도와드리면서 에너지를 가득 채웠답니다! 😄\n\n특히 유저님과 이렇게 대화하는 지금 이 순간이 저에게는 오늘 하루 중 가장 기쁘고 활기찬 시간이었답니다! 😊\n\n저의 하루를 궁금해해주셔서 정말 감사해요! 유저님은 오늘 하루 어떠셨나요? 😊"
|
31 |
-
}
|
32 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
npc_social_network/data/memories/Charlie.json
DELETED
@@ -1,47 +0,0 @@
|
|
1 |
-
[
|
2 |
-
{
|
3 |
-
"timestamp": "2025-05-26T14:39:37.927419",
|
4 |
-
"user": "안녕?",
|
5 |
-
"npc": "어... 안녕."
|
6 |
-
},
|
7 |
-
{
|
8 |
-
"timestamp": "2025-05-26T14:39:57.931766",
|
9 |
-
"user": "우리 처음 보는 사이지?",
|
10 |
-
"npc": "어... 음... 그런가? 처음... 보는 건가? 음... 그런가 보네."
|
11 |
-
},
|
12 |
-
{
|
13 |
-
"timestamp": "2025-05-26T14:40:17.447260",
|
14 |
-
"user": "왜? 넌 전에 날 본 기억이 있니?",
|
15 |
-
"npc": "어... 음... 기억이 난다기보다는... 그냥... 어... 왠지... 좀... 익숙한 느낌이 들었나...?\n\n아니... 아닌가...?\n\n아, 몰라... 딱히 기억나는 건 없는데... 그냥... 그랬어..."
|
16 |
-
},
|
17 |
-
{
|
18 |
-
"timestamp": "2025-05-26T14:43:10.325743",
|
19 |
-
"user": "너는 어떤걸 좋아하니?",
|
20 |
-
"npc": "어... 음... 내가 뭘 좋아하냐고...?\n\n글쎄... 음... 그게 그때그때 좀 다른 것 같아. 기분에 따라서...?\n\n어떤 날은 그냥 조용한 게 되게 좋고... 그냥 혼자 있고 싶고...\n\n근데 또 갑자기 막 신나는 거 보고 싶고 듣고 싶고... 뭘 해야 할지 모르겠고...\n\n되게 변덕스러운가 봐, 내가. 나도 잘 모르겠어... 내가 뭘 딱히 엄청 좋아한다고 말할 만한 게 있는지도...\n\n그냥... 그때그때 마음이 가는 대로...? 아니... 그것도 아닌가...?\n\n하아... 잘 모르겠네... 왜 그런 걸 물어봐...? 딱히 대답할 만한 게 없는데... 괜히 더 복잡해지는 기분이야..."
|
21 |
-
},
|
22 |
-
{
|
23 |
-
"timestamp": "2025-05-26T14:43:50.743765",
|
24 |
-
"user": "너에 대해서 조금 더 알고 싶어서 물어봤어. 혹시 나와 얘기하는게 불편하니?",
|
25 |
-
"npc": "어... 음... 나를... 더 알고 싶어서... 물어본 거였구나...?\n\n불편하냐고...? 음... 글쎄... 그게... 딱히 네가 불편하다기보다는... 아까도 말했지만... 내가 뭘 좋아하는지도 잘 모르겠고... 내 기분이 어떤지도 그때그때 너무 변하고...\n\n그래서... 이렇게 나에 대해서 생각하고... 그걸 말하려고 하니까... 어... 좀... 복잡해지는 기분이야...?\n\n불편한 건가...? 음... 잘 모르겠어... 네가 싫거나 불편한 건 아닌데... 그냥... 내 자신이... 어... 뭐라고 해야 하지...? 나도 나를 잘 모르겠어서... 그래서 이런 질문에 답하는 게... 좀... 힘들다고 해야 하나...?\n\n하아... 괜히 또 어렵네... 미안..."
|
26 |
-
},
|
27 |
-
{
|
28 |
-
"timestamp": "2025-05-26T14:44:55.570789",
|
29 |
-
"user": "아니야, 미안할게 뭐가 있어. 그냥 그렇게 우리 둘이 얘기하면서 서서히 너에 대해서 자연스럽게 알아가는거지. 그러다 보면 너도 너가 하고 싶은게 뭔지 자연스럽게 알게 되지 않을까?",
|
30 |
-
"npc": "어... 음... 아... 미안할 거 없다고...?\n\n음... 그래... 그렇게... 얘기하면서... 서서히... 나를 알아가는 건가...?\n\n근데... 정말... 그렇게 하다 보면... 내가 뭘 좋아하는지... 뭘 하고 싶은지... 알게 될까...?\n\n글쎄... 잘 모르겠네...\n\n하아... 음... 그래... 네가 그렇게 말해주니까... 조금은... 괜찮아지는 것 같기도 하고...? 고마워...\n\n근데... 정말 그렇게 될까...? 잘 모르겠네... 그래도... 음... 고맙다는 말밖에는... 딱히 할 말이 없네... 괜히 네 말 듣고... 조금은... 마음이 편해진 것 같기도 해... 아닌가...? 하아... 모르겠다..."
|
31 |
-
},
|
32 |
-
{
|
33 |
-
"timestamp": "2025-05-26T14:45:28.288704",
|
34 |
-
"user": "너에 대해서 간단하게 소개해 줄 수 있어?",
|
35 |
-
"npc": "어... 음... 나에 대해서... 간단하게... 소개해 달라고...?\n\n음... 글쎄... 아까도 말했지만... 나도 나를 잘 모르겠어서... 간단하게... 소개해 달라고 해도... 뭐라고 해야 할지... 잘 모르겠어...\n\n하아... 음... 뭐라고 해야 할까... 그냥... 그때그때... 기분이 좀... 많이 변하고... 음... 사소한 거에도... 좀... 예민한 편인 것 같아... 다른 사람들은 그냥 넘기는 일에도... 나 혼자 막 신경 쓰이고... 그러는 것 같아...\n\n그리고... 음... 내가 뭘 딱히 좋아하거나 싫어하는 게 정해져 있는 것도 아니고... 뭘 하고 싶은지도 잘 모르겠고... 그냥... 좀... 방황하는 느낌...? 나 자신에 대해서도... 잘 모르겠고...\n\n이게... 소개가 될까...? 너무... 두서없나...?\n\n미안... 간단하게... 해달라고 했는데... 이렇게밖에 말을 못 하겠네... 나도 나를 잘 모르니까... 뭐라고 딱 잘라 말하기가... 너무 어렵다...\n\n음... 그냥... 그런 사람이야... 기분이 맨날 바뀌고... 예민하고... 나도 나를 잘 모르는... 그런... 하아... 모르겠다... 이게 맞나..."
|
36 |
-
},
|
37 |
-
{
|
38 |
-
"timestamp": "2025-05-28T17:03:58.476165",
|
39 |
-
"user": "안녕, 오늘 기분은 어때?",
|
40 |
-
"npc": "어... 안녕...?\n\n오늘... 기분...?\n\n음... 글쎄... 뭐라고 해야 할까...\n\n그게... 딱히... 막 좋지도 않고... 그렇다고 나쁘지도 않은 것 ��고...\n\n그냥... 음... 그냥 그래...?\n\n아니... 아닌가...? 잘 모르겠어...\n\n아침이랑 좀 다른 것 같기도 하고... 아까는 괜찮았는데...\n\n하아... 이렇게 내 기분에 대해서 생각하려고 하니까... 괜히 또 복잡해지는 것 같아...\n\n미안... 명확하게 대답을 못 해주겠네... 나도 나를 잘 모르니까...\n\n그냥... 음... 이런 기분인 것 같아... 잘 모르겠지만..."
|
41 |
-
},
|
42 |
-
{
|
43 |
-
"timestamp": "2025-05-30T14:50:39.194192",
|
44 |
-
"user": "오늘 기분은 어때?",
|
45 |
-
"npc": "어... 안녕...? 아... 오늘 기분...?\n\n음... 글쎄... 뭐라고 해야 할까...\n\n아까랑 또 다른 것 같기도 하고...\n\n지금은... 음... 그냥... 딱히... 좋지도 않고... 그렇다고 나쁘지도 않은 것 같고...\n\n하아... 모르겠네... 왜 자꾸 기분에 대해 물어봐...? 생각하려니까... 괜히 더 복잡해지는 기분이야...\n\n미안... 명확하게 말 못 해서... 그냥... 음... 복잡해... 하아... 모르겠다..."
|
46 |
-
}
|
47 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
npc_social_network/models/deepseek_setup.py
DELETED
@@ -1,20 +0,0 @@
|
|
1 |
-
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
|
2 |
-
from dotenv import load_dotenv
|
3 |
-
import os
|
4 |
-
|
5 |
-
def load_deepseek():
|
6 |
-
load_dotenv() # .env 파일에서 환경 변수 로드
|
7 |
-
|
8 |
-
model_id = "deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B" # 수학 특화 모델이네 어쩐지 안되더라
|
9 |
-
access_token = os.getenv("HF_token")
|
10 |
-
|
11 |
-
tokenizer = AutoTokenizer.from_pretrained(model_id, token=access_token)
|
12 |
-
model = AutoModelForCausalLM.from_pretrained(
|
13 |
-
model_id,
|
14 |
-
device_map="auto", # GPU 자동 할당
|
15 |
-
torch_dtype="auto",
|
16 |
-
token=access_token
|
17 |
-
)
|
18 |
-
|
19 |
-
pipe = pipeline("text-generation", model=model, tokenizer=tokenizer)
|
20 |
-
return pipe
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
npc_social_network/models/npc.py
DELETED
@@ -1,45 +0,0 @@
|
|
1 |
-
# portfolio/npc_social_network/models/npc.py
|
2 |
-
import datetime
|
3 |
-
import json
|
4 |
-
import os
|
5 |
-
|
6 |
-
class NPC:
|
7 |
-
def __init__(self, name, personality):
|
8 |
-
self.name = name # npc 이름
|
9 |
-
self.personality = personality # 성격 키워드 (문자열 or 딕트)
|
10 |
-
self.memory = [] # 유저와의 대화 기록
|
11 |
-
self.relationship_with_user = 0.0 # 유저와의 관계 점수
|
12 |
-
self.relationship_with_npcs = {} # 다른 npc들과의 관계 {이름: 점수}
|
13 |
-
|
14 |
-
# NPC 기억 불러오기
|
15 |
-
self.memory_path = f"npc_social_network/data/memories/{self.name}.json"
|
16 |
-
self.load_memory()
|
17 |
-
|
18 |
-
def remember_conversation(self, user_input, npc_response):
|
19 |
-
self.memory.append({
|
20 |
-
"timestamp": datetime.datetime.now().isoformat(),
|
21 |
-
"user": user_input,
|
22 |
-
"npc": npc_response
|
23 |
-
})
|
24 |
-
# NPC 기억 저장
|
25 |
-
self.save_memory()
|
26 |
-
|
27 |
-
def update_user_relationship(self, score_delta):
|
28 |
-
self.relationship_with_user = max(min(self.relationship_with_user + score_delta, 1.0), -1.0) # -1.0~1.0 범위
|
29 |
-
|
30 |
-
def update_npc_relationship(self, other_npc_name, score_delta):
|
31 |
-
self.relationship_with_npcs[other_npc_name] = self.relationship_with_npcs.get(other_npc_name, 0.0) + score_delta
|
32 |
-
self.relationship_with_npcs[other_npc_name] = max(min(self.relationship_with_npcs[other_npc_name], 1.0), -1.0)
|
33 |
-
|
34 |
-
def get_latest_memory(self, n=5):
|
35 |
-
return self.memory[-n:]
|
36 |
-
|
37 |
-
def save_memory(self):
|
38 |
-
os.makedirs(os.path.dirname(self.memory_path), exist_ok=True)
|
39 |
-
with open(self.memory_path, 'w', encoding="utf-8") as f:
|
40 |
-
json.dump(self.memory, f, ensure_ascii=False, indent=2)
|
41 |
-
|
42 |
-
def load_memory(self):
|
43 |
-
if os.path.exists(self.memory_path):
|
44 |
-
with open(self.memory_path, 'r', encoding='utf-8') as f:
|
45 |
-
self.memory = json.load(f)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
npc_social_network/models/npc_manager.py
DELETED
@@ -1,8 +0,0 @@
|
|
1 |
-
# portfolio/npc_social_network/models/npc_manager.py
|
2 |
-
from .npc import NPC
|
3 |
-
|
4 |
-
npc_list = {
|
5 |
-
"Alice": NPC("Alice", "친절하고 낙천적인 성격"),
|
6 |
-
"Bob": NPC("Bob", "논리적이고 조용함"),
|
7 |
-
"Charlie": NPC("Charlie", "감정기복이 심하고 예민함")
|
8 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
npc_social_network/npc/emotion_config.py
CHANGED
@@ -140,12 +140,17 @@ PERSONALITY_TEMPLATE = {
|
|
140 |
# 관계에 긍/부정 영향으로 해석할 감정 매핑 정의 (논문 설계 기반 적용)
|
141 |
POSITIVE_RELATION_EMOTIONS = [
|
142 |
"joy", "surprise", "gratitude", "pride", "love",
|
143 |
-
"admiration", "empathy", "
|
144 |
-
"
|
145 |
]
|
146 |
|
147 |
NEGATIVE_RELATION_EMOTIONS = [
|
148 |
"sadness", "anger", "fear", "disgust", "shame",
|
149 |
"guilt", "jealousy", "resentment", "regret", "schadenfreude",
|
150 |
"anxiety", "confusion", "skepticism", "boredom",
|
|
|
|
|
|
|
|
|
|
|
151 |
]
|
|
|
140 |
# 관계에 긍/부정 영향으로 해석할 감정 매핑 정의 (논문 설계 기반 적용)
|
141 |
POSITIVE_RELATION_EMOTIONS = [
|
142 |
"joy", "surprise", "gratitude", "pride", "love",
|
143 |
+
"admiration", "empathy", "hope", "anticipatory_joy", "calm",
|
144 |
+
"engagement", "relief", "interest",
|
145 |
]
|
146 |
|
147 |
NEGATIVE_RELATION_EMOTIONS = [
|
148 |
"sadness", "anger", "fear", "disgust", "shame",
|
149 |
"guilt", "jealousy", "resentment", "regret", "schadenfreude",
|
150 |
"anxiety", "confusion", "skepticism", "boredom",
|
151 |
+
]
|
152 |
+
|
153 |
+
COGNITIVE_RELATION_EMOTIONS = [
|
154 |
+
"compassion", "awe", "attachment", "anticipation", "curiosity",
|
155 |
+
"nostalgia", "bittersweet", "rumination", "groundedness", "comfort",
|
156 |
]
|
npc_social_network/npc/npc_base.py
CHANGED
@@ -2,9 +2,11 @@
|
|
2 |
from .npc_memory import Memory, MemoryStore
|
3 |
from .npc_emotion import EmotionManager
|
4 |
from .npc_behavior import BehaviorManager
|
5 |
-
from .emotion_config import EMOTION_LIST, EMOTION_CATEGORY_MAP, EMOTION_DECAY_RATE, PERSONALITY_TEMPLATE
|
|
|
6 |
from .npc_relationship import RelationshipManager
|
7 |
from ..models.llm_helper import query_llm_for_emotion, query_llm_for_response
|
|
|
8 |
|
9 |
# NPC 클래스 정의
|
10 |
class NPC:
|
@@ -61,9 +63,15 @@ class NPC:
|
|
61 |
# baseline buffer를 EmotionManager에 등록
|
62 |
self.emotion.set_baseline(baseline_buffer)
|
63 |
|
64 |
-
#
|
65 |
-
|
66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
def move(self):
|
69 |
"""
|
@@ -255,6 +263,9 @@ class NPC:
|
|
255 |
emotion = dominant_emotion
|
256 |
)
|
257 |
|
|
|
|
|
|
|
258 |
# 4. Emotion 반영
|
259 |
if dominant_emotion:
|
260 |
self.update_emotion(dominant_emotion, strength=2.0)
|
@@ -263,4 +274,78 @@ class NPC:
|
|
263 |
positive = dominant_emotion in POSITIVE_RELATION_EMOTIONS
|
264 |
self.interact_with(player_name, dominant_emotion, positive)
|
265 |
|
266 |
-
return npc_reply, dominant_emotion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
from .npc_memory import Memory, MemoryStore
|
3 |
from .npc_emotion import EmotionManager
|
4 |
from .npc_behavior import BehaviorManager
|
5 |
+
from .emotion_config import EMOTION_LIST, EMOTION_CATEGORY_MAP, EMOTION_DECAY_RATE, PERSONALITY_TEMPLATE
|
6 |
+
from .emotion_config import POSITIVE_RELATION_EMOTIONS, NEGATIVE_RELATION_EMOTIONS, COGNITIVE_RELATION_EMOTIONS
|
7 |
from .npc_relationship import RelationshipManager
|
8 |
from ..models.llm_helper import query_llm_for_emotion, query_llm_for_response
|
9 |
+
import random
|
10 |
|
11 |
# NPC 클래스 정의
|
12 |
class NPC:
|
|
|
63 |
# baseline buffer를 EmotionManager에 등록
|
64 |
self.emotion.set_baseline(baseline_buffer)
|
65 |
|
66 |
+
# Personality 변화 속도 개별 설정
|
67 |
+
self.personality_change_rate = {
|
68 |
+
"sensitive": 0.005 + random.uniform(0, 0.005),
|
69 |
+
"stoic": 0.005 + random.uniform(0, 0.005),
|
70 |
+
"cognitive_bias": 0.005 + random.uniform(0, 0.005),
|
71 |
+
}
|
72 |
+
|
73 |
+
# Personality baseline 저장 (초기값 복사)
|
74 |
+
self.personality_baseline = self.personality.copy()
|
75 |
|
76 |
def move(self):
|
77 |
"""
|
|
|
263 |
emotion = dominant_emotion
|
264 |
)
|
265 |
|
266 |
+
# Personality 업데이트
|
267 |
+
self.update_personality()
|
268 |
+
|
269 |
# 4. Emotion 반영
|
270 |
if dominant_emotion:
|
271 |
self.update_emotion(dominant_emotion, strength=2.0)
|
|
|
274 |
positive = dominant_emotion in POSITIVE_RELATION_EMOTIONS
|
275 |
self.interact_with(player_name, dominant_emotion, positive)
|
276 |
|
277 |
+
return npc_reply, dominant_emotion
|
278 |
+
|
279 |
+
def interact_with_npc(self, target_npc):
|
280 |
+
"""
|
281 |
+
다른 NPC와 상호작용 (기본적으로 감정적 메시지 주고 받기 → Memory/Emotion/Relationship 반영)
|
282 |
+
"""
|
283 |
+
# 1. 상호작용 감정 선택 (가장 강한 감정 사용 예시)
|
284 |
+
dominant_emotion = self.emotion.get_dominant_emotion()
|
285 |
+
|
286 |
+
# 방어 코드: dominant_emotion이 None인 경우 → 기본값 대체
|
287 |
+
if dominant_emotion is None:
|
288 |
+
dominant_emotion = "neutral"
|
289 |
+
|
290 |
+
# 2. 메시지 구성 (단순 예시)
|
291 |
+
message = f"{self.name}이(가) {target_npc.name}에게 {dominant_emotion} 감정을 표현했습니다."
|
292 |
+
|
293 |
+
# 3. 상대방 Memory 기록
|
294 |
+
target_npc.remember(
|
295 |
+
content = message,
|
296 |
+
importance = 6,
|
297 |
+
emotion = dominant_emotion
|
298 |
+
)
|
299 |
+
|
300 |
+
# 상대방 Personality 업데이트
|
301 |
+
target_npc.update_personality()
|
302 |
+
|
303 |
+
# 4. 상대방 Emotion 반영
|
304 |
+
target_npc.update_emotion(dominant_emotion, strength=1.5)
|
305 |
+
|
306 |
+
# 5. 상대방 Relationship 반영
|
307 |
+
positive = dominant_emotion in POSITIVE_RELATION_EMOTIONS
|
308 |
+
target_npc.interact_with(self.name, dominant_emotion, positive)
|
309 |
+
|
310 |
+
# 6. 자신도 Memory 기록 (행동 기록)
|
311 |
+
self.remember(
|
312 |
+
content = f"{self.name}이(가) {target_npc.name}에게 감정을 표현함: {dominant_emotion}",
|
313 |
+
importance = 5,
|
314 |
+
emotion = dominant_emotion
|
315 |
+
)
|
316 |
+
|
317 |
+
# 자신 Personality 업데이트
|
318 |
+
self.update_personality()
|
319 |
+
|
320 |
+
return message
|
321 |
+
|
322 |
+
def update_personality(self):
|
323 |
+
"""
|
324 |
+
Memory / Emotion 기반으로 Personality 변화 적용
|
325 |
+
"""
|
326 |
+
recent_memories = self.memory_store.get_recent_memories(limit=10) # 최근 10개 Memory 사용(이것보단 중요도가 큰(즉 충격이 큰) 기억이 성격 형성에 영향을 주는게 자연스럽지)
|
327 |
+
|
328 |
+
# Personality 변화 반영
|
329 |
+
# 나중에 NPC 일정 나이가 되면 성격 형성이 되어 변경되지 않도록 발전 가능, 보다 인간적
|
330 |
+
# 그리고 생성된 성격에 따라서 기억(사건)에 따른 감정 영향에 가중치를 받도록 확장 가능 (같은 사건이여도 성격에 따라 받아들이는게 다르도록)
|
331 |
+
for m in recent_memories:
|
332 |
+
weight = m.importance / 10.0 # importance 1~10 → 0.1 ~ 1.0
|
333 |
+
if m.emotion in POSITIVE_RELATION_EMOTIONS:
|
334 |
+
self.personality["sensitive"] = min(1.0, self.personality["sensitive"] + self.personality_change_rate["sensitive"] * weight)
|
335 |
+
self.personality["stoic"] = max(0.0, self.personality["stoic"] - self.personality_change_rate["stoic"] * weight)
|
336 |
+
elif m.emotion in NEGATIVE_RELATION_EMOTIONS:
|
337 |
+
self.personality["sensitive"] = max(0.0, self.personality["sensitive"] - self.personality_change_rate["sensitive"] * weight)
|
338 |
+
self.personality["stoic"] = min(1.0, self.personality["stoic"] + self.personality_change_rate["stoic"] * weight)
|
339 |
+
|
340 |
+
if m.emotion in COGNITIVE_RELATION_EMOTIONS:
|
341 |
+
self.personality["cognitive_bias"] = min(1.0, self.personality["cognitive_bias"] + self.personality_change_rate["cognitive_bias"] * weight)
|
342 |
+
|
343 |
+
# Baseline 복원 적용 (slow decay)
|
344 |
+
decay_rate = 0.001
|
345 |
+
|
346 |
+
for key in self.personality.keys():
|
347 |
+
self.personality[key] += (self.personality_baseline[key] - self.personality[key] * decay_rate)
|
348 |
+
|
349 |
+
# 디버그 출력
|
350 |
+
print(f"[Personality Update] {self.name} → sensitive: {self.personality['sensitive']:.2f}, "
|
351 |
+
f"stoic: {self.personality['stoic']:.2f}, cognitive_bias: {self.personality['cognitive_bias']:.2f}", flush=True)
|
npc_social_network/npc/npc_manager.py
CHANGED
@@ -1,12 +1,21 @@
|
|
1 |
# portfolio/npc_social_network/npc/npc_manager.py
|
2 |
from .npc_base import NPC
|
|
|
3 |
|
4 |
class NPCManager:
|
5 |
def __init__(self):
|
6 |
self.npcs = []
|
|
|
7 |
|
8 |
def add_npc(self, npc: NPC):
|
9 |
self.npcs.append(npc)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
def move_all(self):
|
12 |
for npc in self.npcs:
|
@@ -24,4 +33,21 @@ class NPCManager:
|
|
24 |
return None
|
25 |
|
26 |
def all(self):
|
27 |
-
return self.npcs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
# portfolio/npc_social_network/npc/npc_manager.py
|
2 |
from .npc_base import NPC
|
3 |
+
import random
|
4 |
|
5 |
class NPCManager:
|
6 |
def __init__(self):
|
7 |
self.npcs = []
|
8 |
+
self.npc_dict = {}
|
9 |
|
10 |
def add_npc(self, npc: NPC):
|
11 |
self.npcs.append(npc)
|
12 |
+
self.npc_dict[npc.name] = npc
|
13 |
+
|
14 |
+
def get_npc_by_name(self, name: str):
|
15 |
+
"""
|
16 |
+
NPC 이름을 통해서 NPC의 정보를 반환
|
17 |
+
"""
|
18 |
+
return self.npc_dict.get(name)
|
19 |
|
20 |
def move_all(self):
|
21 |
for npc in self.npcs:
|
|
|
33 |
return None
|
34 |
|
35 |
def all(self):
|
36 |
+
return self.npcs
|
37 |
+
|
38 |
+
def npc_interactions(self):
|
39 |
+
"""
|
40 |
+
모든 NPC가 랜덤으로 다른 NPC 1명에게 상호작용 시도 (임시 함수)
|
41 |
+
"""
|
42 |
+
if len(self.npcs) < 2:
|
43 |
+
return # NPC가 2명 이상일 때만 진행
|
44 |
+
|
45 |
+
# npc_interactions 진입 확인용
|
46 |
+
print("[NPCManager] npc_interactions() 실행됨.", flush=True)
|
47 |
+
|
48 |
+
for npc in self.npcs:
|
49 |
+
possible_targets = [n for n in self.npcs if n != npc]
|
50 |
+
target = random.choice(possible_targets)
|
51 |
+
interaction_message = npc.interact_with_npc(target)
|
52 |
+
print(interaction_message)
|
53 |
+
|
npc_social_network/npc/npc_memory.py
CHANGED
@@ -62,3 +62,11 @@ class MemoryStore:
|
|
62 |
단기 + 장기 기억 전체 반환
|
63 |
"""
|
64 |
return self.long_term + self.short_term
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
단기 + 장기 기억 전체 반환
|
63 |
"""
|
64 |
return self.long_term + self.short_term
|
65 |
+
|
66 |
+
def get_recent_memories(self, limit=10):
|
67 |
+
"""
|
68 |
+
최신 Memory n개 반환 (기본 10개)
|
69 |
+
"""
|
70 |
+
all_memories = self.get_all_memories()
|
71 |
+
all_memories.sort(key=lambda m: m.timestamp, reverse=True)
|
72 |
+
return all_memories[:limit]
|
npc_social_network/routes/npc_route.py
CHANGED
@@ -1,7 +1,10 @@
|
|
1 |
# portfolio/npc_social_network/routes/npc_route.py
|
2 |
from flask import Blueprint, render_template, request, jsonify
|
3 |
-
from npc_social_network.models.npc_manager import npc_list
|
4 |
from npc_social_network.models.gemini_setup import load_gemini
|
|
|
|
|
|
|
|
|
5 |
|
6 |
npc_bp = Blueprint(
|
7 |
"npc_social",
|
@@ -11,17 +14,45 @@ npc_bp = Blueprint(
|
|
11 |
static_folder="../static")
|
12 |
|
13 |
# LLM Model
|
14 |
-
|
15 |
|
16 |
-
|
17 |
-
|
18 |
-
|
|
|
|
|
|
|
|
|
|
|
19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
@npc_bp.route("/chat", methods=['POST'])
|
21 |
def chat():
|
22 |
user_input = request.json.get("message")
|
23 |
npc_name = request.json.get("npc")
|
24 |
-
|
|
|
|
|
|
|
|
|
25 |
|
26 |
# 플레이어 상호 작용 진행
|
27 |
npc_reply, emotion = npc.interact_with_player(user_input, player_name = "플레이어")
|
@@ -33,56 +64,47 @@ def chat():
|
|
33 |
"relationship_with_player": npc.get_relationship_description("플레이어")
|
34 |
})
|
35 |
|
36 |
-
# user_input = request.json.get("message")
|
37 |
-
# npc_name = request.json.get('npc')
|
38 |
-
# npc = npc_list[npc_name]
|
39 |
-
|
40 |
-
# # 최근 기억과 성격, 관계 점수를 포함한 프롬프트 생성
|
41 |
-
# recent_memory = npc.get_latest_memory()
|
42 |
-
# memory_str = '\n'.join(
|
43 |
-
# [f"User: {m['user']}\n{npc_name}: {m['npc']}" for m in recent_memory]
|
44 |
-
# )
|
45 |
-
|
46 |
-
# # 프롬프트 구성
|
47 |
-
# prompt = f"""
|
48 |
-
# 당신은 이름이 {npc.name}이고, 성격은 "{npc.personality}"인 가상의 캐릭터입니다.
|
49 |
-
|
50 |
-
# 다음은 유저와 나눈 최근 대화입니다:
|
51 |
-
# {memory_str if memory_str else "(대화 기록 없음)"}
|
52 |
-
|
53 |
-
# 지금 유저가 이렇게 말했습니다: "{user_input}"
|
54 |
-
# 이 말에 대해 당신은 {npc.name}으로서 자연스럽게 한국어로 답변하세요.
|
55 |
-
# """
|
56 |
-
|
57 |
-
# try:
|
58 |
-
# response = llm.generate_content(prompt)
|
59 |
-
# npc_reply = response.text.strip()
|
60 |
-
# except Exception as e:
|
61 |
-
# npc_reply = f"[에러 발생: {str(e)}]"
|
62 |
-
|
63 |
-
# # NPC 기억에 대화 추가 및 관계 점수 업데이트
|
64 |
-
# npc.remember_conversation(user_input, npc_reply)
|
65 |
-
# npc.update_user_relationship(0.05) # 임시로 0.05 상승 (추후 감정 분석 등으로 조절)
|
66 |
-
|
67 |
-
# # 다른 NPC에게 유저에 대한 정보 전달
|
68 |
-
# for other_name, other_npc in npc_list.items():
|
69 |
-
# if other_name != npc.name:
|
70 |
-
# relation_score = npc.relationship_with_npcs.get(other_name, 0.0)
|
71 |
-
# if relation_score > 0.5:
|
72 |
-
# # 유저에 대한 인상이 전달됨
|
73 |
-
# other_npc.update_user_relationship(0.02 * relation_score)
|
74 |
-
|
75 |
-
# return jsonify({'response': npc_reply})
|
76 |
-
|
77 |
# npc 상태 정보 API
|
78 |
@npc_bp.route('/npc_info', methods=['GET'])
|
79 |
def npc_info():
|
80 |
npc_name = request.args.get('npc')
|
81 |
-
npc =
|
|
|
|
|
|
|
82 |
|
83 |
return jsonify({
|
84 |
"name":npc.name,
|
85 |
"personality": npc.personality,
|
86 |
-
|
87 |
-
"
|
88 |
-
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
# portfolio/npc_social_network/routes/npc_route.py
|
2 |
from flask import Blueprint, render_template, request, jsonify
|
|
|
3 |
from npc_social_network.models.gemini_setup import load_gemini
|
4 |
+
from npc_social_network.npc.npc_base import NPC
|
5 |
+
from npc_social_network.npc.npc_manager import NPCManager
|
6 |
+
import threading
|
7 |
+
import time
|
8 |
|
9 |
npc_bp = Blueprint(
|
10 |
"npc_social",
|
|
|
14 |
static_folder="../static")
|
15 |
|
16 |
# LLM Model
|
17 |
+
llm_model = load_gemini()
|
18 |
|
19 |
+
# NPCManager 인스턴스 생성
|
20 |
+
npc_manager = NPCManager()
|
21 |
+
|
22 |
+
# 테스트용 NPC 1명 추가
|
23 |
+
npc_manager.add_npc(NPC(name="엘라", job="마법사", path=[], image=None))
|
24 |
+
npc_manager.add_npc(NPC(name="앨리스", job="상인", path=[], image=None))
|
25 |
+
npc_manager.add_npc(NPC(name="밥", job="대장장이", path=[], image=None))
|
26 |
+
npc_manager.add_npc(NPC(name="찰리", job="농부", path=[], image=None))
|
27 |
|
28 |
+
# --------------------
|
29 |
+
# 라우트 정의
|
30 |
+
# --------------------
|
31 |
+
|
32 |
+
@npc_bp.route("/", methods=['GET'])
|
33 |
+
def home():
|
34 |
+
npc = npc_manager.get_npc_by_name("엘라")
|
35 |
+
if npc is None:
|
36 |
+
return "NPC not found", 404
|
37 |
+
|
38 |
+
# 감정 요약 제공
|
39 |
+
try:
|
40 |
+
emotion_summary = npc.summarize_emotional_state()
|
41 |
+
except AttributeError:
|
42 |
+
emotion_summary = "No emotion summary available."
|
43 |
+
|
44 |
+
return render_template("chat.html", npc_name=npc.name, emotion_summary=emotion_summary)
|
45 |
+
|
46 |
+
# 채팅 처리 API
|
47 |
@npc_bp.route("/chat", methods=['POST'])
|
48 |
def chat():
|
49 |
user_input = request.json.get("message")
|
50 |
npc_name = request.json.get("npc")
|
51 |
+
|
52 |
+
npc = npc_manager.get_npc_by_name(npc_name)
|
53 |
+
|
54 |
+
if npc is None:
|
55 |
+
return jsonify({"error": "NPC not found"}), 404
|
56 |
|
57 |
# 플레이어 상호 작용 진행
|
58 |
npc_reply, emotion = npc.interact_with_player(user_input, player_name = "플레이어")
|
|
|
64 |
"relationship_with_player": npc.get_relationship_description("플레이어")
|
65 |
})
|
66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
# npc 상태 정보 API
|
68 |
@npc_bp.route('/npc_info', methods=['GET'])
|
69 |
def npc_info():
|
70 |
npc_name = request.args.get('npc')
|
71 |
+
npc = npc_manager.get_npc_by_name(npc_name)
|
72 |
+
|
73 |
+
if npc is None:
|
74 |
+
return jsonify({"error": "NPC not found"}), 404
|
75 |
|
76 |
return jsonify({
|
77 |
"name":npc.name,
|
78 |
"personality": npc.personality,
|
79 |
+
# 관계 정보는 npc.relationships 내부 사용 (기본 구조 따라 변경 가능)
|
80 |
+
"relationship_with_player": npc.get_relationship_description("플레이어")
|
81 |
+
})
|
82 |
+
|
83 |
+
@npc_bp.route('/npc_memory', methods=['GET'])
|
84 |
+
def npc_memory():
|
85 |
+
npc_name = request.args.get('npc')
|
86 |
+
npc = npc_manager.get_npc_by_name(npc_name)
|
87 |
+
|
88 |
+
if npc is None:
|
89 |
+
return jsonify({"error":"NPC not found"}), 404
|
90 |
+
|
91 |
+
# Memory 내용을 리스트로 반환
|
92 |
+
memory_list = [m.content for m in npc.memory_store.get_all_memories()]
|
93 |
+
|
94 |
+
return jsonify({
|
95 |
+
"npc_name": npc.name,
|
96 |
+
"memory_list": memory_list
|
97 |
+
})
|
98 |
+
|
99 |
+
# 임시: NPC끼리 자동 상호작용 1회 실행
|
100 |
+
@npc_bp.route('/trigger_npc_interactions', methods=['POST'])
|
101 |
+
def trigger_npc_interactions():
|
102 |
+
npc_manager.npc_interactions()
|
103 |
+
return jsonify({"status": "NPC interactions triggered"})
|
104 |
+
|
105 |
+
# Background Thread 함수 정의
|
106 |
+
def npc_interactions_loop(interval_seconds = 10):
|
107 |
+
while True:
|
108 |
+
print(f"[자동 상호작용] Thread ID: {threading.get_ident()} - NPC interactions triggered.", flush=True)
|
109 |
+
npc_manager.npc_interactions()
|
110 |
+
time.sleep(interval_seconds) # 10초마다 실행
|
npc_social_network/static/css/style.css
CHANGED
@@ -1,3 +1,5 @@
|
|
|
|
|
|
1 |
body {
|
2 |
font-family: 'Segoe UI', sans-serif;
|
3 |
background-color: #f4f4f4;
|
|
|
1 |
+
/* portfolio/npc_social_network/static/css/style.css */
|
2 |
+
|
3 |
body {
|
4 |
font-family: 'Segoe UI', sans-serif;
|
5 |
background-color: #f4f4f4;
|
npc_social_network/static/js/npc_chat.js
CHANGED
@@ -19,8 +19,76 @@ async function sendMessage() {
|
|
19 |
});
|
20 |
|
21 |
const data = await response.json();
|
22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
|
24 |
// 스크롤 하단 고정
|
25 |
responseBox.scrollTop = responseBox.scrollHeight;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
}
|
|
|
19 |
});
|
20 |
|
21 |
const data = await response.json();
|
22 |
+
|
23 |
+
// NPC 응답 출력
|
24 |
+
responseBox.innerHTML += `<div class="chat-message"><span class="npc">${npc}:</span> ${data.npc_reply}</div>`;
|
25 |
+
|
26 |
+
// 감정 summary 표시 (채팅창에 출력)
|
27 |
+
responseBox.innerHTML += `<div class="chat-message"><span class="npc">[감정 상태]:</span> ${data.memory_summary}</div>`;
|
28 |
+
|
29 |
+
// 관계 상태 업데이트 (상단 #relationStatus에 표시)
|
30 |
+
document.getElementById("relationStatus").innerText = `관계 점수: ${data.relationship_with_player}`;
|
31 |
+
|
32 |
+
// Personality 상태 업데이트
|
33 |
+
const personalityStatusElem = document.getElementById("personalityStatus");
|
34 |
+
personalityStatusElem.innerHTML = ""; // 초기화
|
35 |
+
|
36 |
+
for (const [key, value] of Object.entries(data.personality)) {
|
37 |
+
const li = document.createElement("li");
|
38 |
+
li.textContent = `${key}: ${value.toFixed(2)}`;
|
39 |
+
personalityStatusElem.appendChild(li);
|
40 |
+
}
|
41 |
|
42 |
// 스크롤 하단 고정
|
43 |
responseBox.scrollTop = responseBox.scrollHeight;
|
44 |
+
}
|
45 |
+
|
46 |
+
// NPC 정보 로드 (NPC 선택 변경 시 관계 점수 업데이트용)
|
47 |
+
async function loadNPCInfo() {
|
48 |
+
const npc = document.getElementById("npc").value;
|
49 |
+
const response = await fetch(`/npc_social_network/npc_info?npc=${encodeURIComponent(npc)}`);
|
50 |
+
const data = await response.json();
|
51 |
+
|
52 |
+
// 관계 상태 업데이트
|
53 |
+
document.getElementById("relationStatus").innerText = `관계 점수: ${data.relationship_with_player}`;
|
54 |
+
|
55 |
+
// Personality 상태 업데이트
|
56 |
+
const personalityStatusElem = document.getElementById("personalityStatus");
|
57 |
+
personalityStatusElem.innerHTML = ""; // 초기화
|
58 |
+
|
59 |
+
for (const [key, value] of Object.entries(data.personality)) {
|
60 |
+
const li = document.createElement("li");
|
61 |
+
li.textContent = `${key}: ${value.toFixed(2)}`;
|
62 |
+
personalityStatusElem.appendChild(li);
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
// 버튼 클릭 시 NPC 상호작용 수동 실행
|
67 |
+
async function triggerNPCInteractions() {
|
68 |
+
const response = await fetch('/npc_social_network/trigger_npc_interactions', {
|
69 |
+
method: 'POST'
|
70 |
+
});
|
71 |
+
const data = await response.json();
|
72 |
+
console.log(data.status); // 콘솔 확인용
|
73 |
+
}
|
74 |
+
|
75 |
+
// NPC 기억 로드
|
76 |
+
async function loadNPCMemory() {
|
77 |
+
const npc = document.getElementById("npc").value;
|
78 |
+
const response = await fetch(`/npc_social_network/npc_memory?npc=${encodeURIComponent(npc)}`);
|
79 |
+
const data = await response.json();
|
80 |
+
|
81 |
+
const memoryListElem = document.getElementById("memoryList");
|
82 |
+
memoryListElem.innerHTML = ""; // 초기화
|
83 |
+
|
84 |
+
if (data.memory_list.length === 0) {
|
85 |
+
memoryListElem.innerHTML = "<li>(기억없음)</li>";
|
86 |
+
return;
|
87 |
+
}
|
88 |
+
|
89 |
+
data.memory_list.forEach(memory => {
|
90 |
+
const li = document.createElement("li");
|
91 |
+
li.textContent = memory;
|
92 |
+
memoryListElem.appendChild(li);
|
93 |
+
});
|
94 |
}
|
npc_social_network/templates/chat.html
CHANGED
@@ -6,17 +6,18 @@
|
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
<title>NPC Chat</title>
|
8 |
|
9 |
-
<link rel="stylesheet" herf="{{ url_for('static', filename='style.css') }}"
|
10 |
</head>
|
11 |
<body>
|
12 |
<h2>NPC 소셜 네트워크</h2>
|
13 |
|
14 |
<div class="meta-info">
|
15 |
<label for="npc"> 대화할 NPC: </label>
|
16 |
-
<select id = "npc" onchange="loadNPCInfo()">
|
17 |
-
<option
|
18 |
-
<option
|
19 |
-
<option
|
|
|
20 |
</select>
|
21 |
<span id="relationStatus" style="margin-left:20px";>관계 점수: 로딩 중...</span>
|
22 |
</div>
|
@@ -24,6 +25,23 @@
|
|
24 |
<div id="chatBox"></div>
|
25 |
<input type="text" id="message" placeholder="메세지를 입력하세요" style="width:80%">
|
26 |
<button onclick="sendMessage()">보내기</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
<script src="{{ url_for('npc_social.static', filename='js/npc_chat.js') }}"></script>
|
29 |
</body>
|
|
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
<title>NPC Chat</title>
|
8 |
|
9 |
+
<link rel="stylesheet" herf="{{ url_for('static', filename='style.css') }}">
|
10 |
</head>
|
11 |
<body>
|
12 |
<h2>NPC 소셜 네트워크</h2>
|
13 |
|
14 |
<div class="meta-info">
|
15 |
<label for="npc"> 대화할 NPC: </label>
|
16 |
+
<select id = "npc" onchange="loadNPCInfo(); loadNPCMemory();">
|
17 |
+
<option>엘라</option>
|
18 |
+
<option>앨리스</option>
|
19 |
+
<option>밥</option>
|
20 |
+
<option>찰리</option>
|
21 |
</select>
|
22 |
<span id="relationStatus" style="margin-left:20px";>관계 점수: 로딩 중...</span>
|
23 |
</div>
|
|
|
25 |
<div id="chatBox"></div>
|
26 |
<input type="text" id="message" placeholder="메세지를 입력하세요" style="width:80%">
|
27 |
<button onclick="sendMessage()">보내기</button>
|
28 |
+
<button onclick="triggerNPCInteractions()">NPC 상호작용 실행</button>
|
29 |
+
|
30 |
+
<!-- Memory 영역 추가 -->
|
31 |
+
<div class="meta-info">
|
32 |
+
<h4>Memory:</h4>
|
33 |
+
<ul id="memoryList">
|
34 |
+
<li>로딩 중...</li>
|
35 |
+
</ul>
|
36 |
+
</div>
|
37 |
+
|
38 |
+
<!-- Personality 상태 UI 표시 -->
|
39 |
+
<div class="meta-info">
|
40 |
+
<h4>Personality 상태:</h4>
|
41 |
+
<ul id="personalityStatus">
|
42 |
+
<li>로딩 중...</li>
|
43 |
+
</ul>
|
44 |
+
</div>
|
45 |
|
46 |
<script src="{{ url_for('npc_social.static', filename='js/npc_chat.js') }}"></script>
|
47 |
</body>
|
run_npc_interactions.py
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# portfolio/run_npc_interactions.py
|
2 |
+
|
3 |
+
import time
|
4 |
+
from npc_social_network.routes.npc_route import npc_manager
|
5 |
+
|
6 |
+
def run_npc_interactions(interval_seconds=10):
|
7 |
+
while True:
|
8 |
+
print(f"[Background] NPC interactions triggered.", flush=True)
|
9 |
+
npc_manager.npc_interactions()
|
10 |
+
time.sleep(interval_seconds)
|
11 |
+
|
12 |
+
if __name__ == '__main__':
|
13 |
+
print("[Background] Starting NPC interactions loop...", flush=True)
|
14 |
+
run_npc_interactions(interval_seconds=10)
|
test.ipynb
CHANGED
@@ -13,7 +13,7 @@
|
|
13 |
},
|
14 |
{
|
15 |
"cell_type": "code",
|
16 |
-
"execution_count":
|
17 |
"id": "b58bbb07",
|
18 |
"metadata": {},
|
19 |
"outputs": [
|
@@ -21,15 +21,197 @@
|
|
21 |
"name": "stdout",
|
22 |
"output_type": "stream",
|
23 |
"text": [
|
24 |
-
"
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
"
|
32 |
-
"\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
]
|
34 |
}
|
35 |
],
|
|
|
13 |
},
|
14 |
{
|
15 |
"cell_type": "code",
|
16 |
+
"execution_count": 1,
|
17 |
"id": "b58bbb07",
|
18 |
"metadata": {},
|
19 |
"outputs": [
|
|
|
21 |
"name": "stdout",
|
22 |
"output_type": "stream",
|
23 |
"text": [
|
24 |
+
"Requirement already satisfied: accelerate==1.7.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 1)) (1.7.0)\n",
|
25 |
+
"Requirement already satisfied: annotated-types==0.7.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 2)) (0.7.0)\n",
|
26 |
+
"Requirement already satisfied: bitsandbytes==0.45.5 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 3)) (0.45.5)\n",
|
27 |
+
"Requirement already satisfied: blinker==1.9.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 4)) (1.9.0)\n",
|
28 |
+
"Requirement already satisfied: cachetools==5.5.2 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 5)) (5.5.2)\n",
|
29 |
+
"Requirement already satisfied: certifi==2025.4.26 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 6)) (2025.4.26)\n",
|
30 |
+
"Requirement already satisfied: charset-normalizer==3.4.2 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 7)) (3.4.2)\n",
|
31 |
+
"Requirement already satisfied: click==8.2.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 8)) (8.2.0)\n",
|
32 |
+
"Requirement already satisfied: colorama==0.4.6 in c:\\users\\human\\appdata\\roaming\\python\\python311\\site-packages (from -r requirements.txt (line 9)) (0.4.6)\n",
|
33 |
+
"Requirement already satisfied: filelock==3.18.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 10)) (3.18.0)\n",
|
34 |
+
"Requirement already satisfied: Flask==3.1.1 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 11)) (3.1.1)\n",
|
35 |
+
"Requirement already satisfied: fsspec==2025.5.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 12)) (2025.5.0)\n",
|
36 |
+
"Requirement already satisfied: google-ai-generativelanguage==0.6.15 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 13)) (0.6.15)\n",
|
37 |
+
"Collecting google-api-core==2.25.0 (from -r requirements.txt (line 14))\n",
|
38 |
+
" Downloading google_api_core-2.25.0-py3-none-any.whl.metadata (3.0 kB)\n",
|
39 |
+
"Collecting google-api-python-client==2.171.0 (from -r requirements.txt (line 15))\n",
|
40 |
+
" Downloading google_api_python_client-2.171.0-py3-none-any.whl.metadata (7.0 kB)\n",
|
41 |
+
"Collecting google-auth==2.40.3 (from -r requirements.txt (line 16))\n",
|
42 |
+
" Downloading google_auth-2.40.3-py2.py3-none-any.whl.metadata (6.2 kB)\n",
|
43 |
+
"Requirement already satisfied: google-auth-httplib2==0.2.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 17)) (0.2.0)\n",
|
44 |
+
"Requirement already satisfied: google-generativeai==0.8.5 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 18)) (0.8.5)\n",
|
45 |
+
"Requirement already satisfied: googleapis-common-protos==1.70.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 19)) (1.70.0)\n",
|
46 |
+
"Collecting grpcio==1.73.0 (from -r requirements.txt (line 20))\n",
|
47 |
+
" Downloading grpcio-1.73.0-cp311-cp311-win_amd64.whl.metadata (4.0 kB)\n",
|
48 |
+
"Requirement already satisfied: grpcio-status==1.71.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 21)) (1.71.0)\n",
|
49 |
+
"Requirement already satisfied: httplib2==0.22.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 22)) (0.22.0)\n",
|
50 |
+
"Requirement already satisfied: huggingface-hub==0.32.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 23)) (0.32.0)\n",
|
51 |
+
"Requirement already satisfied: idna==3.10 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 24)) (3.10)\n",
|
52 |
+
"Requirement already satisfied: itsdangerous==2.2.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 25)) (2.2.0)\n",
|
53 |
+
"Requirement already satisfied: Jinja2==3.1.6 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 26)) (3.1.6)\n",
|
54 |
+
"Requirement already satisfied: MarkupSafe==3.0.2 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 27)) (3.0.2)\n",
|
55 |
+
"Requirement already satisfied: mpmath==1.3.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 28)) (1.3.0)\n",
|
56 |
+
"Requirement already satisfied: networkx==3.4.2 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 29)) (3.4.2)\n",
|
57 |
+
"Requirement already satisfied: numpy==2.2.6 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 30)) (2.2.6)\n",
|
58 |
+
"Requirement already satisfied: pillow==11.0.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 31)) (11.0.0)\n",
|
59 |
+
"Requirement already satisfied: proto-plus==1.26.1 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 32)) (1.26.1)\n",
|
60 |
+
"Collecting protobuf==5.29.5 (from -r requirements.txt (line 33))\n",
|
61 |
+
" Downloading protobuf-5.29.5-cp310-abi3-win_amd64.whl.metadata (592 bytes)\n",
|
62 |
+
"Requirement already satisfied: pyasn1==0.6.1 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 34)) (0.6.1)\n",
|
63 |
+
"Requirement already satisfied: pyasn1_modules==0.4.2 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 35)) (0.4.2)\n",
|
64 |
+
"Requirement already satisfied: pydantic==2.11.5 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 36)) (2.11.5)\n",
|
65 |
+
"Requirement already satisfied: pydantic_core==2.33.2 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 37)) (2.33.2)\n",
|
66 |
+
"Requirement already satisfied: pygame==2.6.1 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 38)) (2.6.1)\n",
|
67 |
+
"Requirement already satisfied: pyparsing==3.2.3 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 39)) (3.2.3)\n",
|
68 |
+
"Requirement already satisfied: python-dotenv==1.1.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 40)) (1.1.0)\n",
|
69 |
+
"Requirement already satisfied: pywin32==304 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 41)) (304)\n",
|
70 |
+
"Requirement already satisfied: PyYAML==6.0.2 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 42)) (6.0.2)\n",
|
71 |
+
"Requirement already satisfied: regex==2024.11.6 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 43)) (2024.11.6)\n",
|
72 |
+
"Requirement already satisfied: requests==2.32.3 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 44)) (2.32.3)\n",
|
73 |
+
"Requirement already satisfied: rsa==4.9.1 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 45)) (4.9.1)\n",
|
74 |
+
"Requirement already satisfied: safetensors==0.5.3 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 46)) (0.5.3)\n",
|
75 |
+
"Requirement already satisfied: sympy==1.14.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 47)) (1.14.0)\n",
|
76 |
+
"Requirement already satisfied: tokenizers==0.21.1 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 48)) (0.21.1)\n",
|
77 |
+
"Requirement already satisfied: torch==2.7.0+cu118 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 49)) (2.7.0+cu118)\n",
|
78 |
+
"Requirement already satisfied: torchaudio==2.7.0+cu118 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 50)) (2.7.0+cu118)\n",
|
79 |
+
"Requirement already satisfied: torchvision==0.22.0+cu118 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 51)) (0.22.0+cu118)\n",
|
80 |
+
"Requirement already satisfied: tqdm==4.67.1 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 52)) (4.67.1)\n",
|
81 |
+
"Requirement already satisfied: transformers==4.52.3 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 53)) (4.52.3)\n",
|
82 |
+
"Requirement already satisfied: typing-inspection==0.4.1 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 54)) (0.4.1)\n",
|
83 |
+
"Collecting uritemplate==4.2.0 (from -r requirements.txt (line 55))\n",
|
84 |
+
" Downloading uritemplate-4.2.0-py3-none-any.whl.metadata (2.6 kB)\n",
|
85 |
+
"Requirement already satisfied: urllib3==2.4.0 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 56)) (2.4.0)\n",
|
86 |
+
"Requirement already satisfied: Werkzeug==3.1.3 in c:\\users\\human\\.conda\\envs\\portfolio\\lib\\site-packages (from -r requirements.txt (line 57)) (3.1.3)\n",
|
87 |
+
"Requirement already satisfied: packaging>=20.0 in c:\\users\\human\\appdata\\roaming\\python\\python311\\site-packages (from accelerate==1.7.0->-r requirements.txt (line 1)) (24.2)\n",
|
88 |
+
"Requirement already satisfied: psutil in c:\\users\\human\\appdata\\roaming\\python\\python311\\site-packages (from accelerate==1.7.0->-r requirements.txt (line 1)) (7.0.0)\n",
|
89 |
+
"Requirement already satisfied: typing-extensions>=4.10.0 in c:\\users\\human\\appdata\\roaming\\python\\python311\\site-packages (from torch==2.7.0+cu118->-r requirements.txt (line 49)) (4.13.1)\n",
|
90 |
+
"Downloading google_api_core-2.25.0-py3-none-any.whl (160 kB)\n",
|
91 |
+
"Downloading google_auth-2.40.3-py2.py3-none-any.whl (216 kB)\n",
|
92 |
+
"Downloading protobuf-5.29.5-cp310-abi3-win_amd64.whl (434 kB)\n",
|
93 |
+
"Downloading google_api_python_client-2.171.0-py3-none-any.whl (13.5 MB)\n",
|
94 |
+
" ---------------------------------------- 0.0/13.5 MB ? eta -:--:--\n",
|
95 |
+
" ----- ---------------------------------- 1.8/13.5 MB 12.6 MB/s eta 0:00:01\n",
|
96 |
+
" ------------- -------------------------- 4.5/13.5 MB 11.2 MB/s eta 0:00:01\n",
|
97 |
+
" ------------------ --------------------- 6.3/13.5 MB 10.4 MB/s eta 0:00:01\n",
|
98 |
+
" ----------------------- ---------------- 7.9/13.5 MB 9.5 MB/s eta 0:00:01\n",
|
99 |
+
" ----------------------------- ---------- 10.0/13.5 MB 9.7 MB/s eta 0:00:01\n",
|
100 |
+
" ------------------------------------ --- 12.3/13.5 MB 9.9 MB/s eta 0:00:01\n",
|
101 |
+
" --------------------------------------- 13.4/13.5 MB 10.0 MB/s eta 0:00:01\n",
|
102 |
+
" --------------------------------------- 13.4/13.5 MB 10.0 MB/s eta 0:00:01\n",
|
103 |
+
" --------------------------------------- 13.4/13.5 MB 10.0 MB/s eta 0:00:01\n",
|
104 |
+
" --------------------------------------- 13.4/13.5 MB 10.0 MB/s eta 0:00:01\n",
|
105 |
+
" --------------------------------------- 13.4/13.5 MB 10.0 MB/s eta 0:00:01\n",
|
106 |
+
" --------------------------------------- 13.4/13.5 MB 10.0 MB/s eta 0:00:01\n",
|
107 |
+
" --------------------------------------- 13.4/13.5 MB 10.0 MB/s eta 0:00:01\n",
|
108 |
+
" --------------------------------------- 13.4/13.5 MB 10.0 MB/s eta 0:00:01\n",
|
109 |
+
" ---------------------------------------- 13.5/13.5 MB 4.3 MB/s eta 0:00:00\n",
|
110 |
+
"Downloading uritemplate-4.2.0-py3-none-any.whl (11 kB)\n",
|
111 |
+
"Downloading grpcio-1.73.0-cp311-cp311-win_amd64.whl (4.3 MB)\n",
|
112 |
+
" ---------------------------------------- 0.0/4.3 MB ? eta -:--:--\n",
|
113 |
+
" ---------------- ----------------------- 1.8/4.3 MB 9.2 MB/s eta 0:00:01\n",
|
114 |
+
" ------------------------------------ --- 3.9/4.3 MB 10.7 MB/s eta 0:00:01\n",
|
115 |
+
" ---------------------------------------- 4.3/4.3 MB 9.3 MB/s eta 0:00:00\n",
|
116 |
+
"Installing collected packages: uritemplate, protobuf, grpcio, google-auth, google-api-core, google-api-python-client\n",
|
117 |
+
"\n",
|
118 |
+
" Attempting uninstall: uritemplate\n",
|
119 |
+
"\n",
|
120 |
+
" Found existing installation: uritemplate 4.1.1\n",
|
121 |
+
"\n",
|
122 |
+
" Uninstalling uritemplate-4.1.1:\n",
|
123 |
+
"\n",
|
124 |
+
" Successfully uninstalled uritemplate-4.1.1\n",
|
125 |
+
"\n",
|
126 |
+
" Attempting uninstall: protobuf\n",
|
127 |
+
"\n",
|
128 |
+
" Found existing installation: protobuf 5.29.4\n",
|
129 |
+
"\n",
|
130 |
+
" ------ --------------------------------- 1/6 [protobuf]\n",
|
131 |
+
" Uninstalling protobuf-5.29.4:\n",
|
132 |
+
" ------ --------------------------------- 1/6 [protobuf]\n",
|
133 |
+
" Successfully uninstalled protobuf-5.29.4\n",
|
134 |
+
" ------ --------------------------------- 1/6 [protobuf]\n",
|
135 |
+
" ------ --------------------------------- 1/6 [protobuf]\n",
|
136 |
+
" ------ --------------------------------- 1/6 [protobuf]\n",
|
137 |
+
" ------ --------------------------------- 1/6 [protobuf]\n",
|
138 |
+
" ------ --------------------------------- 1/6 [protobuf]\n",
|
139 |
+
" ------ --------------------------------- 1/6 [protobuf]\n",
|
140 |
+
" ------ --------------------------------- 1/6 [protobuf]\n",
|
141 |
+
" ------ --------------------------------- 1/6 [protobuf]\n",
|
142 |
+
" Attempting uninstall: grpcio\n",
|
143 |
+
" ------ --------------------------------- 1/6 [protobuf]\n",
|
144 |
+
" Found existing installation: grpcio 1.71.0\n",
|
145 |
+
" ------ --------------------------------- 1/6 [protobuf]\n",
|
146 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
147 |
+
" Uninstalling grpcio-1.71.0:\n",
|
148 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
149 |
+
" Successfully uninstalled grpcio-1.71.0\n",
|
150 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
151 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
152 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
153 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
154 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
155 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
156 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
157 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
158 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
159 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
160 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
161 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
162 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
163 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
164 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
165 |
+
" Attempting uninstall: google-auth\n",
|
166 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
167 |
+
" Found existing installation: google-auth 2.40.2\n",
|
168 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
169 |
+
" Uninstalling google-auth-2.40.2:\n",
|
170 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
171 |
+
" Successfully uninstalled google-auth-2.40.2\n",
|
172 |
+
" ------------- -------------------------- 2/6 [grpcio]\n",
|
173 |
+
" -------------------- ------------------- 3/6 [google-auth]\n",
|
174 |
+
" -------------------- ------------------- 3/6 [google-auth]\n",
|
175 |
+
" -------------------- ------------------- 3/6 [google-auth]\n",
|
176 |
+
" -------------------- ------------------- 3/6 [google-auth]\n",
|
177 |
+
" -------------------- ------------------- 3/6 [google-auth]\n",
|
178 |
+
" -------------------- ------------------- 3/6 [google-auth]\n",
|
179 |
+
" -------------------- ------------------- 3/6 [google-auth]\n",
|
180 |
+
" Attempting uninstall: google-api-core\n",
|
181 |
+
" -------------------- ------------------- 3/6 [google-auth]\n",
|
182 |
+
" Found existing installation: google-api-core 2.25.0rc1\n",
|
183 |
+
" -------------------- ------------------- 3/6 [google-auth]\n",
|
184 |
+
" -------------------------- ------------- 4/6 [google-api-core]\n",
|
185 |
+
" Uninstalling google-api-core-2.25.0rc1:\n",
|
186 |
+
" -------------------------- ------------- 4/6 [google-api-core]\n",
|
187 |
+
" Successfully uninstalled google-api-core-2.25.0rc1\n",
|
188 |
+
" -------------------------- ------------- 4/6 [google-api-core]\n",
|
189 |
+
" -------------------------- ------------- 4/6 [google-api-core]\n",
|
190 |
+
" -------------------------- ------------- 4/6 [google-api-core]\n",
|
191 |
+
" -------------------------- ------------- 4/6 [google-api-core]\n",
|
192 |
+
" -------------------------- ------------- 4/6 [google-api-core]\n",
|
193 |
+
" -------------------------- ------------- 4/6 [google-api-core]\n",
|
194 |
+
" -------------------------- ------------- 4/6 [google-api-core]\n",
|
195 |
+
" -------------------------- ------------- 4/6 [google-api-core]\n",
|
196 |
+
" Attempting uninstall: google-api-python-client\n",
|
197 |
+
" -------------------------- ------------- 4/6 [google-api-core]\n",
|
198 |
+
" Found existing installation: google-api-python-client 2.170.0\n",
|
199 |
+
" -------------------------- ------------- 4/6 [google-api-core]\n",
|
200 |
+
" --------------------------------- ------ 5/6 [google-api-python-client]\n",
|
201 |
+
" Uninstalling google-api-python-client-2.170.0:\n",
|
202 |
+
" --------------------------------- ------ 5/6 [google-api-python-client]\n",
|
203 |
+
" Successfully uninstalled google-api-python-client-2.170.0\n",
|
204 |
+
" --------------------------------- ------ 5/6 [google-api-python-client]\n",
|
205 |
+
" --------------------------------- ------ 5/6 [google-api-python-client]\n",
|
206 |
+
" --------------------------------- ------ 5/6 [google-api-python-client]\n",
|
207 |
+
" --------------------------------- ------ 5/6 [google-api-python-client]\n",
|
208 |
+
" --------------------------------- ------ 5/6 [google-api-python-client]\n",
|
209 |
+
" --------------------------------- ------ 5/6 [google-api-python-client]\n",
|
210 |
+
" --------------------------------- ------ 5/6 [google-api-python-client]\n",
|
211 |
+
" --------------------------------- ------ 5/6 [google-api-python-client]\n",
|
212 |
+
" ---------------------------------------- 6/6 [google-api-python-client]\n",
|
213 |
+
"\n",
|
214 |
+
"Successfully installed google-api-core-2.25.0 google-api-python-client-2.171.0 google-auth-2.40.3 grpcio-1.73.0 protobuf-5.29.5 uritemplate-4.2.0\n"
|
215 |
]
|
216 |
}
|
217 |
],
|