gaeunseo commited on
Commit
a835322
ยท
verified ยท
1 Parent(s): aa86eef

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +60 -35
app.py CHANGED
@@ -7,48 +7,61 @@ ai_message = "Hello, how can I help you?"
7
 
8
  def stream_human_message():
9
  """
10
- Human ๋ฉ”์‹œ์ง€๋ฅผ ํ•œ ๊ธ€์ž์”ฉ ํƒ€์ดํ•‘ํ•˜๋Š” ํšจ๊ณผ๋กœ ์ŠคํŠธ๋ฆฌ๋ฐํ•ฉ๋‹ˆ๋‹ค.
11
- ๊ฐ ๊ธ€์ž๋Š” <span> ํƒœ๊ทธ๋กœ ๊ฐ์‹ธ์ง€๋ฉฐ, Start Typing ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑ๋˜์–ด ๊ธฐ์กด์˜ โœ‚๏ธ ์œ„์น˜ ๋ฐ ํšŒ์ƒ‰ ์ฒ˜๋ฆฌ ๋“ฑ์ด ๋ฆฌ์…‹๋ฉ๋‹ˆ๋‹ค.
 
12
  """
13
- # ์ดˆ๊ธฐ ๋งํ’์„  ์ปจํ…Œ์ด๋„ˆ ์ƒ์„ฑ
14
- html = "<div id='human_message' class='speech-bubble human'>"
15
- yield html # ์ดˆ๊ธฐ ์ปจํ…Œ์ด๋„ˆ ์ถœ๋ ฅ
16
- # ํ•œ ๊ธ€์ž์”ฉ ์ถ”๊ฐ€
17
- for i, ch in enumerate(human_message):
18
- html += f"<span data-index='{i}'>{ch}</span>"
19
- yield html
20
- time.sleep(0.05) # ํƒ€์ดํ•‘ ํšจ๊ณผ ๋”œ๋ ˆ์ด
21
- html += "</div>"
 
 
 
 
 
22
  yield html
23
 
 
 
 
 
 
 
 
24
  with gr.Blocks() as demo:
25
- # ํŽ˜์ด์ง€ ์ƒ๋‹จ์— ๋‹จ ํ•œ ๋ฒˆ ๋กœ๋“œ๋  ์Šคํฌ๋ฆฝํŠธ: ์ด๋ฒคํŠธ ์œ„์ž„ ๋ฐฉ์‹์œผ๋กœ ํด๋ฆญ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
26
  gr.HTML(
27
  """
28
  <script>
29
- // ๋ฌธ์„œ ์ „์ฒด์—์„œ ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ๊ฐ์ง€ํ•˜์—ฌ, human ๋งํ’์„  ๋‚ด์˜ span ์š”์†Œ์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
30
  document.addEventListener("click", function(event) {
31
- // human ๋งํ’์„  ๋‚ด๋ถ€์˜ data-index ์†์„ฑ์„ ๊ฐ€์ง„ span์ด ํด๋ฆญ๋œ ๊ฒฝ์šฐ์—๋งŒ ์ฒ˜๋ฆฌ
32
  if (event.target && event.target.matches("div.speech-bubble.human span[data-index]")) {
33
  var span = event.target;
34
- var container = span.parentNode;
35
- // ๊ธฐ์กด์— ์‚ฝ์ž…๋œ ๋ชจ๋“  scissor ์š”์†Œ ์ œ๊ฑฐ
 
36
  var oldScissors = container.querySelectorAll("span.scissor");
37
  oldScissors.forEach(function(s) { s.remove(); });
38
- // ๋ชจ๋“  span์˜ ํšŒ์ƒ‰ ์Šคํƒ€์ผ ์ œ๊ฑฐ
39
  var spans = container.querySelectorAll("span[data-index]");
40
  spans.forEach(function(s) { s.style.color = ''; });
41
- // ํด๋ฆญํ•œ span ๋’ค์— โœ‚๏ธ ์•„์ด์ฝ˜์„ ์‚ฝ์ž… (ํด๋ž˜์Šค "scissor" ์ถ”๊ฐ€)
42
  var scissor = document.createElement('span');
43
  scissor.textContent = 'โœ‚๏ธ';
44
- scissor.classList.add('scissor');
45
  container.insertBefore(scissor, span.nextSibling);
46
- // ํด๋ฆญํ•œ ๋ฌธ์ž ์ดํ›„์˜ ๋ชจ๋“  span์— ๋Œ€ํ•ด ํ…์ŠคํŠธ ์ƒ‰์ƒ์„ ํšŒ์ƒ‰์œผ๋กœ ๋ณ€๊ฒฝ
47
- var cutIndex = parseInt(span.getAttribute('data-index'));
48
  spans.forEach(function(s) {
49
- var idx = parseInt(s.getAttribute('data-index'));
50
  if (idx > cutIndex) {
51
- s.style.color = 'grey';
52
  }
53
  });
54
  }
@@ -57,17 +70,18 @@ with gr.Blocks() as demo:
57
  """
58
  )
59
 
60
- # CSS ์Šคํƒ€์ผ: ๋งํ’์„  ๋ชจ์–‘ ๋ฐ ๋ฐฐ์น˜ ์„ค์ •
61
  gr.HTML(
62
  """
63
  <style>
64
- /* ์ „์ฒด ์ฑ„ํŒ… ์˜์—ญ: ์„ธ๋กœ ๋ฐฐ์น˜ */
65
  .chat-container {
66
  display: flex;
67
  flex-direction: column;
68
  gap: 10px;
 
69
  }
70
- /* ๊ณตํ†ต ๋งํ’์„  ์Šคํƒ€์ผ */
71
  .speech-bubble {
72
  position: relative;
73
  padding: 10px 15px;
@@ -77,12 +91,12 @@ with gr.Blocks() as demo:
77
  font-size: 16px;
78
  line-height: 1.4;
79
  }
80
- /* Human ๋งํ’์„ : ์˜ค๋ฅธ์ชฝ ์ •๋ ฌ, ์ดˆ๋ก์ƒ‰ ๋ฐฐ๊ฒฝ ๋ฐ ์˜ค๋ฅธ์ชฝ ๊ผฌ๋ฆฌ */
81
  .human {
82
  background: #d0f0d0;
83
- align-self: flex-end;
84
  margin-right: 10px;
85
  }
 
86
  .human::after {
87
  content: "";
88
  position: absolute;
@@ -92,12 +106,12 @@ with gr.Blocks() as demo:
92
  border-style: solid;
93
  border-color: transparent transparent transparent #d0f0d0;
94
  }
95
- /* AI ๋งํ’์„ : ์™ผ์ชฝ ์ •๋ ฌ, ํšŒ์ƒ‰ ๋ฐฐ๊ฒฝ ๋ฐ ์™ผ์ชฝ ๊ผฌ๋ฆฌ */
96
  .ai {
97
  background: #e0e0e0;
98
- align-self: flex-start;
99
  margin-left: 10px;
100
  }
 
101
  .ai::after {
102
  content: "";
103
  position: absolute;
@@ -105,20 +119,31 @@ with gr.Blocks() as demo:
105
  top: 10px;
106
  border-width: 10px 10px 10px 0;
107
  border-style: solid;
108
- border-color: transparent #e0f0e0 transparent transparent;
 
 
 
 
 
109
  }
110
  </style>
111
  """
112
  )
113
 
114
  gr.Markdown("## Chat Interface")
 
115
  with gr.Column(elem_classes="chat-container"):
116
- # ์œ„์ชฝ: Human ๋งํ’์„  (์ŠคํŠธ๋ฆฌ๋ฐ ์—…๋ฐ์ดํŠธ๋จ)
117
  human_bubble = gr.HTML("")
118
- # ์•„๋ž˜์ชฝ: AI ๋งํ’์„  (๊ณ ์ • ๋ฉ”์‹œ์ง€)
119
- ai_bubble = gr.HTML(f"<div id='ai_message' class='speech-bubble ai'>{ai_message}</div>")
 
 
 
 
 
 
120
 
121
- # ํƒ€์ดํ•‘ ์‹œ์ž‘ ๋ฒ„ํŠผ: ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ stream_human_message๊ฐ€ ํ˜ธ์ถœ๋˜์–ด ์ƒˆ HTML์„ ์ƒ์„ฑ (์ฆ‰, โœ‚๏ธ ์œ„์น˜๊ฐ€ ๋ฆฌ์…‹๋จ)
122
  start_button = gr.Button("Start Typing")
123
  start_button.click(fn=stream_human_message, outputs=human_bubble)
124
 
 
7
 
8
  def stream_human_message():
9
  """
10
+ Human ๋ฉ”์‹œ์ง€๋ฅผ ํ•œ ๊ธ€์ž์”ฉ ํƒ€์ดํ•‘ ํšจ๊ณผ๋กœ ์ŠคํŠธ๋ฆฌ๋ฐํ•˜๋ฉด์„œ,
11
+ ์˜ค๋ฅธ์ชฝ์— ๐Ÿง‘ ์ด๋ชจํ‹ฐ์ฝ˜์ด ์žˆ๋Š” wrapper ์•ˆ์— ๋งํ’์„ ์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
12
+ Start Typing์„ ๋ˆ„๋ฅด๋ฉด ์ด์ „ ์ƒํƒœ๊ฐ€ ๋ฆฌ์…‹๋ฉ๋‹ˆ๋‹ค.
13
  """
14
+ bubble_content = ""
15
+ # wrapper ์‹œ์ž‘: ์ „์ฒด ๋„ˆ๋น„๋ฅผ ์ฑ„์šฐ๊ณ , ์˜ค๋ฅธ์ชฝ ์ •๋ ฌํ•˜๋Š” flex ์ปจํ…Œ์ด๋„ˆ
16
+ wrapper_start = (
17
+ """<div class="human-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-end; gap: 5px; width: 100%;">"""
18
+ )
19
+ # ๋งํ’์„  ์ปจํ…Œ์ด๋„ˆ ์‹œ์ž‘
20
+ bubble_start = """<div id="human_message" class="speech-bubble human">"""
21
+ bubble_end = "</div>"
22
+ # ์˜ค๋ฅธ์ชฝ์— ๊ณ ์ •๋  ์ด๋ชจํ‹ฐ์ฝ˜
23
+ emoji_html = "<div class='emoji'>๐Ÿง‘</div>"
24
+ wrapper_end = "</div>"
25
+
26
+ # ์ดˆ๊ธฐ ๋นˆ ๋งํ’์„ ๊ณผ ์ด๋ชจํ‹ฐ์ฝ˜ ์ถœ๋ ฅ
27
+ html = wrapper_start + bubble_start + bubble_end + emoji_html + wrapper_end
28
  yield html
29
 
30
+ # ํ•œ ๊ธ€์ž์”ฉ ์ถ”๊ฐ€ํ•˜์—ฌ streaming ์—…๋ฐ์ดํŠธ
31
+ for i, ch in enumerate(human_message):
32
+ bubble_content += f"<span data-index='{i}'>{ch}</span>"
33
+ current_html = wrapper_start + bubble_start + bubble_content + bubble_end + emoji_html + wrapper_end
34
+ yield current_html
35
+ time.sleep(0.05)
36
+
37
  with gr.Blocks() as demo:
38
+ # ์ „์—ญ์ ์œผ๋กœ ํ•œ ๋ฒˆ๋งŒ ๋กœ๋“œ๋˜๋Š” ์Šคํฌ๋ฆฝํŠธ: ์ด๋ฒคํŠธ ์œ„์ž„์œผ๋กœ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€๋˜๋Š” span์˜ ํด๋ฆญ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
39
  gr.HTML(
40
  """
41
  <script>
42
+ // human ๋งํ’์„  ๋‚ด๋ถ€์˜ span[data-index]์— ๋Œ€ํ•ด ํด๋ฆญ ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ
43
  document.addEventListener("click", function(event) {
 
44
  if (event.target && event.target.matches("div.speech-bubble.human span[data-index]")) {
45
  var span = event.target;
46
+ // ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด human ๋งํ’์„  ์ปจํ…Œ์ด๋„ˆ ์„ ํƒ
47
+ var container = span.closest("div.speech-bubble.human");
48
+ // ๊ธฐ์กด์— ์‚ฝ์ž…๋œ โœ‚๏ธ ์ด๋ชจํ‹ฐ์ฝ˜(ํด๋ž˜์Šค "scissor") ์ œ๊ฑฐ
49
  var oldScissors = container.querySelectorAll("span.scissor");
50
  oldScissors.forEach(function(s) { s.remove(); });
51
+ // ๋ชจ๋“  span์˜ ์ƒ‰์ƒ์„ ์›๋ž˜ ์ƒ‰์œผ๋กœ ์ดˆ๊ธฐํ™”
52
  var spans = container.querySelectorAll("span[data-index]");
53
  spans.forEach(function(s) { s.style.color = ''; });
54
+ // ํด๋ฆญํ•œ span ๋ฐ”๋กœ ๋’ค์— โœ‚๏ธ ์ด๋ชจํ‹ฐ์ฝ˜ ์‚ฝ์ž…
55
  var scissor = document.createElement('span');
56
  scissor.textContent = 'โœ‚๏ธ';
57
+ scissor.classList.add("scissor");
58
  container.insertBefore(scissor, span.nextSibling);
59
+ // ํด๋ฆญํ•œ span ์ดํ›„์˜ ๋ชจ๋“  span ํ…์ŠคํŠธ ์ƒ‰์ƒ์„ ํšŒ์ƒ‰์œผ๋กœ ๋ณ€๊ฒฝ
60
+ var cutIndex = parseInt(span.getAttribute("data-index"));
61
  spans.forEach(function(s) {
62
+ var idx = parseInt(s.getAttribute("data-index"));
63
  if (idx > cutIndex) {
64
+ s.style.color = "grey";
65
  }
66
  });
67
  }
 
70
  """
71
  )
72
 
73
+ # CSS ์Šคํƒ€์ผ ์ ์šฉ
74
  gr.HTML(
75
  """
76
  <style>
77
+ /* ์ „์ฒด ์ฑ„ํŒ… ์˜์—ญ: ์„ธ๋กœ ๋ฐฐ์น˜ ๋ฐ ๊ฐ„๊ฒฉ ํ™•๋ณด */
78
  .chat-container {
79
  display: flex;
80
  flex-direction: column;
81
  gap: 10px;
82
+ width: 100%;
83
  }
84
+ /* ๋งํ’์„  ๊ธฐ๋ณธ ์Šคํƒ€์ผ */
85
  .speech-bubble {
86
  position: relative;
87
  padding: 10px 15px;
 
91
  font-size: 16px;
92
  line-height: 1.4;
93
  }
94
+ /* Human ๋งํ’์„ : ๋ฐฐ๊ฒฝ์ƒ‰ ๋ฐ ์˜ค๋ฅธ์ชฝ์— ๋ฐฐ์น˜ (wrapper์—์„œ ์ •๋ ฌ) */
95
  .human {
96
  background: #d0f0d0;
 
97
  margin-right: 10px;
98
  }
99
+ /* Human ๋งํ’์„ ์˜ ๊ผฌ๋ฆฌ (์˜ค๋ฅธ์ชฝ) */
100
  .human::after {
101
  content: "";
102
  position: absolute;
 
106
  border-style: solid;
107
  border-color: transparent transparent transparent #d0f0d0;
108
  }
109
+ /* AI ๋งํ’์„ : ๋ฐฐ๊ฒฝ์ƒ‰ ๋ฐ ์™ผ์ชฝ์— ๋ฐฐ์น˜ (wrapper์—์„œ ์ •๋ ฌ) */
110
  .ai {
111
  background: #e0e0e0;
 
112
  margin-left: 10px;
113
  }
114
+ /* AI ๋งํ’์„ ์˜ ๊ผฌ๋ฆฌ (์™ผ์ชฝ) */
115
  .ai::after {
116
  content: "";
117
  position: absolute;
 
119
  top: 10px;
120
  border-width: 10px 10px 10px 0;
121
  border-style: solid;
122
+ border-color: transparent #e0e0e0 transparent transparent;
123
+ }
124
+ /* Emoji ์•„์ด์ฝ˜ ์Šคํƒ€์ผ */
125
+ .emoji {
126
+ font-size: 24px;
127
+ line-height: 1;
128
  }
129
  </style>
130
  """
131
  )
132
 
133
  gr.Markdown("## Chat Interface")
134
+
135
  with gr.Column(elem_classes="chat-container"):
136
+ # Human ๋ฉ”์‹œ์ง€ (์œ„์ชฝ, streaming)
137
  human_bubble = gr.HTML("")
138
+ # AI ๋ฉ”์‹œ์ง€ (์•„๋ž˜์ชฝ, ๊ณ ์ •) - ์™ผ์ชฝ์— ๐Ÿค– ์ด๋ชจํ‹ฐ์ฝ˜์„ ์ถ”๊ฐ€ํ•˜๋Š” wrapper ์‚ฌ์šฉ
139
+ ai_html = """
140
+ <div class="ai-wrapper" style="display: flex; align-items: flex-end; justify-content: flex-start; gap: 5px; width: 100%;">
141
+ <div class="emoji">๐Ÿค–</div>
142
+ <div id="ai_message" class="speech-bubble ai">""" + ai_message + """</div>
143
+ </div>
144
+ """
145
+ ai_bubble = gr.HTML(ai_html)
146
 
 
147
  start_button = gr.Button("Start Typing")
148
  start_button.click(fn=stream_human_message, outputs=human_bubble)
149