changeset 48:46daba6e3cf4

Few python scrtips to show how to use asychio.
author MrJuneJune <me@mrjunejune.com>
date Sat, 13 Dec 2025 14:23:02 -0800
parents 829623189a57
children 2b11e0449042
files asyncio_threads/README.md asyncio_threads/bank_question/README.md asyncio_threads/bank_question/main.py asyncio_threads/bucket_questions/README.md asyncio_threads/bucket_questions/main.py asyncio_threads/bucket_questions/single_thread_async.py asyncio_threads/database/README.md asyncio_threads/database/main.py asyncio_threads/frontend/README.md asyncio_threads/frontend/index.html asyncio_threads/frontend/longest_subinteger.py asyncio_threads/frontend/main.js asyncio_threads/inference/README.md asyncio_threads/inference/main.py asyncio_threads/stop_token/main.py
diffstat 15 files changed, 648 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/README.md	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,3 @@
+# Asyncio
+
+Wanted to know how asycio works in python so made few questiosn that I would probably ask others to show how to do use these.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/bank_question/README.md	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,34 @@
+# Bank Account
+
+You are given a simplified multi-threaded “bank account” implementation used by one of our infrastructure teams. The current code processes deposits concurrently but produces incorrect results.
+
+```
+class BankAccount:
+    def __init__(self, balance):
+        self.balance = balance
+
+    def deposit(self, amount):
+        new_balance = self.balance + amount  # read
+        time.sleep(0.1)                      # simulate delay
+        self.balance = new_balance           # write
+
+account = BankAccount(0)
+
+with ThreadPoolExecutor(max_workers=2) as executor:
+    futures = [
+        executor.submit(account.deposit, 500),
+        executor.submit(account.deposit, 700),
+    ]
+```
+
+Task
+
+Identify the concurrency bugs in the implementation.
+
+Explain why these bugs occur.
+
+Fix the implementation without modifying the existing BankAccount class directly.
+
+You may use subclassing (Python) or embedding (Go-style composition), depending on the language you choose.
+
+Ensure your solution is thread-safe and produces the correct final balance (1200).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/bank_question/main.py	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,43 @@
+# # Bank Account
+# 
+# You are given a simplified multi-threaded “bank account” implementation used by one of our infrastructure teams. The current code processes deposits concurrently but produces incorrect results.
+# 
+# Task
+# 
+# Identify the concurrency bugs in the implementation.
+# 
+# Explain why these bugs occur.
+# 
+# Fix the implementation without modifying the existing BankAccount class directly.
+# 
+# You may use subclassing (Python) or embedding (Go-style composition), depending on the language you choose.
+# 
+# Ensure your solution is thread-safe and produces the correct final balance (1200).
+
+from threading import Lock
+from concurrent.futures import ThreadPoolExecutor
+import time
+
+class BankAccount:
+    def __init__(self, balance):
+        self.balance = balance
+        self._lock = Lock()
+
+    def deposit(self, amount):
+        while (self._lock.locked()):
+            pass
+        self._lock.acquire()
+        new_balance = self.balance + amount  # read
+        time.sleep(0.1)                      # simulate delay
+        self.balance = new_balance           # write
+        self._lock.release()
+
+
+account = BankAccount(0)
+
+with ThreadPoolExecutor(max_workers=2) as executor:
+    futures = [
+        executor.submit(account.deposit, 500),
+        executor.submit(account.deposit, 700),
+    ]
+print(account.balance)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/bucket_questions/README.md	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,14 @@
+# Bucket questions 
+
+## Context
+
+Please implement two functions: refillTokenBucket and useTokens. Two classes are already defined: DistributedCache and TokenBucket.
+
+refillTokenBucket(user_id): Refill tokens for the specified user's bucket.
+
+useTokens(user_id, tokens): Check if there are enough tokens in the specified user's token bucket. If so, update the remaining token count and return true; otherwise, return false. You are required to use instances of the existing classes to implement these functions.
+
+## Requirements
+
+Use the API provided by DistributedCache to get and update the user's TokenBucket.
+Implement the functionality using existing class instances and ensure the behavior is correct across multiple calls
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/bucket_questions/main.py	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,65 @@
+import asyncio
+from concurrent.futures import ThreadPoolExecutor
+from typing import Dict
+
+class DistributedCache:
+
+    def __init__(self):
+        self._user_bucket_map: Dict[str, TokenBucket] = {}
+
+
+    def get_bucket(self, user_id: str):
+        return self._user_bucket_map[user_id]
+
+    def set_bucket(self, user_id: str):
+        self._user_bucket_map[user_id] = TokenBucket()
+
+
+INITIAL_VALUES = 10
+
+class TokenBucket:
+
+    def __init__(self, initial_values = INITIAL_VALUES):
+        self._tokens = initial_values
+        self._refill_values = initial_values
+        self._lock = asyncio.Lock()
+
+    def get_tokens(self):
+        print(self._tokens)
+        return self._tokens
+
+    async def consume_tokens(self, token: int):
+        async with self._lock:
+            if self.get_tokens() < token:
+                return False
+
+            await asyncio.sleep(1)
+            self._tokens -=  token
+            return True 
+
+    async def refill_tokens(self):
+        async with self._lock:
+            self._tokens = self._refill_values
+
+
+cache = DistributedCache()
+
+user_1 = "JUNE"
+user_2 = "VICTOR"
+cache.set_bucket(user_1)
+cache.set_bucket(user_2)
+
+async def refill_token_bucket(user_id: str):
+    await cache.get_bucket(user_id).refill_tokens()
+
+async def use_tokens(user_id: str, tokens: int):
+    return await cache.get_bucket(user_id).consume_tokens(tokens)
+
+def run():
+    with ThreadPoolExecutor(max_workers=3) as thread:
+        thread.submit(asyncio.run, use_tokens(user_1, 10))
+        thread.submit(asyncio.run, use_tokens(user_1, 10))
+        thread.submit(asyncio.run, use_tokens(user_2, 10))
+
+if __name__ == "__main__":
+    run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/bucket_questions/single_thread_async.py	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,67 @@
+import asyncio
+from typing import Dict
+
+class DistributedCache:
+
+    def __init__(self):
+        self._user_bucket_map: Dict[str, TokenBucket] = {}
+
+
+    def get_bucket(self, user_id: str):
+        return self._user_bucket_map[user_id]
+
+    def set_bucket(self, user_id: str):
+        self._user_bucket_map[user_id] = TokenBucket()
+
+
+INITIAL_VALUES = 10
+
+class TokenBucket:
+
+    def __init__(self, initial_values = INITIAL_VALUES):
+        self._tokens = initial_values
+        self._refill_values = initial_values
+        self._lock = asyncio.Lock()
+
+    def get_tokens(self):
+        print(self._tokens)
+        return self._tokens
+
+    async def consume_tokens(self, token: int):
+        async with self._lock:
+            if self.get_tokens() < token:
+                return False
+
+            await asyncio.sleep(1)
+            self._tokens -=  token
+            return True 
+
+    async def refill_tokens(self):
+        async with self._lock:
+            self._tokens = self._refill_values
+
+
+cache = DistributedCache()
+
+user_1 = "JUNE"
+user_2 = "VICTOR"
+cache.set_bucket(user_1)
+cache.set_bucket(user_2)
+
+async def refill_token_bucket(user_id: str):
+    await cache.get_bucket(user_id).refill_tokens()
+
+async def use_tokens(user_id: str, tokens: int):
+    return await cache.get_bucket(user_id).consume_tokens(tokens)
+
+async def run():
+    return_value = await asyncio.gather(
+      use_tokens(user_1, 10),
+      refill_token_bucket(user_1),
+      use_tokens(user_1, 10),
+      use_tokens(user_2, 10),
+    )
+    print(return_value)
+
+if __name__ == "__main__":
+    asyncio.run(run())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/database/README.md	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,19 @@
+# Design an in-memory key-value database that supports the following commands and fully handles nested transactions.
+
+## Core Operations:
+SET <key> <value>: Sets the value for a key.
+
+GET <key>: Returns the value for a key.
+
+UNSET <key>: Removes the key and its value.
+
+## Transaction Operations:
+BEGIN: Starts a new transaction scope. If a transaction is already active, this starts a nested transaction.
+
+COMMIT: Applies all changes made in the current transaction scope and all its active nested transactions to the parent scope (or to the main database state if no parent exists). After a successful commit, the transaction scope is closed.
+
+ ROLLBACK: Discards all changes made in the current transaction scope and all its active nested transactions, restoring the state to what it was when the BEGIN command was issued for the current scope. After a successful rollback, the transaction scope is closed.
+
+## Implementation Goal:
+
+Design the primary data structures and outline the logic for SET, COMMIT, and ROLLBACK to ensure nested transactions operate correctly. Explain how the state of the database is managed across multiple transaction layers.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/database/main.py	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,16 @@
+class Database:
+
+    def __init__(self):
+        self.db = {}
+        self.state = 'idle'
+
+    def set(self, key, value):
+        self.db[key] = value
+
+    def get(self, key):
+        return self.db[key]
+
+    def unset(self, key):
+        del self.db[key]
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/frontend/README.md	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,13 @@
+Frontend Task
+
+1. Component Implementation: Enhanced TypeAhead (Good?)
+
+Develop a TypeAhead input component that filters a dataset based on user entry. Implement a debounce mechanism to optimize performance by limiting the frequency of function execution during typing. Apply text processing logic to identify and visually highlight the specific substrings within the results that match the user's input.
+
+2. Algorithmic Challenge: Increasing Subsequence(Okay?)
+
+Analyze a given array of integers and write a function to determine the length of the longest subsequent growing sequence contained within the array.
+
+3. Frontend Architecture: AI Prompt and Context Interface
+
+Construct a dual-pane user interface featuring a prompt history sidebar on the left and a main content area on the right for input and context display. Implement data fetching from the OpenAI API. Develop state management logic where submitting a new prompt triggers an API call, and selecting a specific prompt from the history injects the corresponding past response to serve as the context for the subsequent request.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/frontend/index.html	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,7 @@
+<HTML>
+  <body>
+    <input id="search" type="text"></input>
+    <div id="suggestion"></div>
+  </body>
+  <script src="./main.js"></script>
+</HTML>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/frontend/longest_subinteger.py	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,53 @@
+# Analyze a given array of integers and write a function to determine the length of the longest subsequent growing sequence contained within the array.
+
+
+# 1,2,3,4,2,1,3
+#             |
+
+# Example 1:
+# 
+# Input: nums = [10,9,2,5,3,7,101,18]
+#   [(2, 1), 3, 5, 7, 9, 10, 18, 101]
+#    |
+# Output: 4
+
+# Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
+
+# Example 2:
+# 
+# Input: nums = [0,1,0,3,2,3]
+# Output: 4
+# Example 3:
+# 
+# Input: nums = [7,7,7,7,7,7,7]
+# Output: 1
+
+
+# Longest Increasing Subsequence
+def main(nums):
+    ans = 0
+    cache = set()
+
+    def dfs(val, pos, curr_ans):
+        nonlocal ans
+
+        if ((val, pos, curr_ans) in cache):
+            return
+
+        if pos > len(nums):
+            return
+        if nums[pos] > val:
+            curr_ans += 1
+        else:
+            ans = max(ans, curr_ans)
+            return 
+        for i in range(pos, len(nums)):
+            dfs(nums[pos], i, curr_ans)
+
+    for i in range(len(nums)):
+        dfs(float("-inf"), i, 0)
+
+    return ans
+
+
+print(main([10,9,2,5,3,7,101,18]))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/frontend/main.js	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,135 @@
+// Develop a TypeAhead input component that filters a dataset based on user entry. Implement a debounce mechanism to optimize performance by limiting the frequency of function execution during typing. Apply text processing logic to identify and visually highlight the specific substrings within the results that match the user's input.
+//
+
+
+// 3. text processing logic which ones...
+
+
+let previousId = 0;
+const inputEle = document.getElementById("search");
+const divSuggestion = document.getElementById("suggestion");
+const tries = {}
+const userNames = [
+  "Ephemeral",
+  "Mellifluous",
+  "Ubiquitous",
+  "Voracious",
+  "Serendipity",
+  "Quixotic",
+  "Languid",
+  "Ephemeral",
+  "Susurrus",
+  "Ebullient",
+  "Petrichor",
+  "Ineluctable",
+  "Platitude",
+  "Ennui",
+  "Axiom",
+  "Cacophony",
+  "Halcyon",
+  "Ineffable",
+  "Juxtapose",
+  "Lugubrious",
+  "Nefarious",
+  "Ostracize",
+  "Pernicious",
+  "Ruminate",
+  "Solipsism",
+  "Taciturn",
+  "Wanderlust",
+  "Zephyr",
+  "Discombobulate",
+  "Esoteric",
+  "Incendiary",
+  "Kudos",
+  "Misanthrope",
+  "Pensive",
+  "Quagmire",
+  "Ramify",
+  "Stymie",
+  "Unctuous",
+  "Vex",
+  "Wistful",
+  "Banal",
+  "Dichotomy",
+  "Fecund",
+  "Garrulous",
+  "Hegemony",
+  "Imbroglio",
+  "Knell",
+  "Mirth",
+  "Obfuscate",
+  "Parsimonious",
+]
+
+function createTries(word) {
+  curr = tries
+  for (let letter of word.toLowerCase()) {
+    if (!curr[letter]) {
+      curr[letter] = {};
+    } 
+    if (!curr[letter]["#"]) {
+      curr[letter]["#"] = [] 
+      curr[letter]["#"].push(word)
+    } else {
+      curr[letter]["#"].push(word)
+    }
+
+    curr = curr[letter];
+  }
+}
+
+userNames.forEach((word) => createTries(word));
+
+function findWord(word) {
+  const results = new Set()
+
+  function dfs(currTries, currWordPosition, fuzz_search, curr_sequential, max_sequential = 0) {
+    if (currWordPosition === word.length || fuzz_search > 1) {
+      console.log(currTries, currWordPosition, fuzz_search, curr_sequential, max_sequential)
+      if (max_sequential >= 2) {
+        currTries['#'].forEach((word) => results.add(word));
+        console.log("hello")
+      }
+      return;
+    }
+    letter = word[currWordPosition].toLowerCase();
+    Object.keys(currTries).forEach((key) => {
+      if (key === '#') return;
+      dfs(currTries[key], 
+        currWordPosition + 1, 
+        (key != letter) ?  fuzz_search + 1 : fuzz_search,
+        (key == letter) ?  curr_sequential + 1 : 0,
+        Math.max((key == letter) ?  curr_sequential + 1 : 0, max_sequential)
+      );
+    })
+  }
+
+  dfs(tries, 0, 0, 0)
+  return results;
+}
+function populateSuggestions(inputValue) {
+  const words = findWord(inputValue); 
+
+  divSuggestion.innerHTML = ""; 
+  for (let word of words) {
+    let res = "";
+    const lowerInput = inputValue.toLowerCase(); 
+    for (let letter of word) {
+      if (lowerInput.includes(letter.toLowerCase())) {
+        res += `<span style="color: red;">${letter}</span>`;
+      } else {
+        res += letter;
+      }
+    }    
+    divSuggestion.innerHTML += res + "<br>"; 
+  }
+}
+
+let i = 0
+inputEle.addEventListener('keypress', () => {
+  if (previousId) {
+    clearTimeout(previousId);
+  }
+  previousId = setTimeout(() => populateSuggestions(inputEle.value), 500); // 1, 2, 3
+})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/inference/README.md	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,30 @@
+Inference Questions
+
+Context
+
+You are tasked with building a simplified inference engine component responsible for handling incoming user requests for a large language model (LLM). To optimize throughput and GPU utilization, the engine must batch multiple requests together, run the inference call once per batch, and then deconstruct the results to return token-level output to the individual users.
+
+Objective
+
+Complete the provided Python class, BatchInferenceEngine by implementing the methods necessary to:
+Queue incoming user requests.
+Process a batch when the queue reaches a defined batch size.
+
+Simulate the token-level output from an LLM and correctly associate each generated token with its original request.
+
+
+Task Requirements
+
+
+
+
+
+Implement the logic for $enqueue\_request$.
+
+
+
+Implement the logic for $\_process\_batch$.
+
+
+
+Demonstrate the usage by creating 7 unique requests and enqueueing them one by one. Show the state of the queue and the processed tokens after each batch run.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/inference/main.py	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,107 @@
+from typing import List, Dict, Any, Optional
+
+class UserRequest:
+    """Represents an incoming user request."""
+    def __init__(self, request_id: int, prompt: str):
+        self.request_id = request_id
+        self.prompt = prompt
+        self.output_tokens: List[str] = [] # Stores tokens as they are generated
+
+    def __repr__(self):
+        return f"Request(ID={self.request_id}, Prompt='{self.prompt[:20]}...', Tokens={len(self.output_tokens)})"
+
+# --- Mock LLM Interface ---
+def mock_inference_call(prompts: List[str], batch_id: int) -> Dict[str, Any]:
+    """
+    Simulates the call to the underlying LLM/GPU.
+
+    In a real scenario, this returns generated tokens and associated metadata.
+    For this mock, we return a flat list of tokens, one for each request
+    in the batch, and the batch ID for verification.
+
+    The length of the tokens list MUST equal the length of the prompts list.
+    """
+    print(f"  [INFERENCE] Running Batch {batch_id} with {len(prompts)} requests...")
+    results = {
+        'batch_id': batch_id,
+        # Simulate generating a single new token for each request in the batch
+        'generated_tokens': [
+            f"token_{i+1}_of_{batch_id}" for i, _ in enumerate(prompts)
+        ]
+    }
+    return results
+# -------------------------
+
+
+class BatchInferenceEngine:
+    """
+    A simplified inference engine that handles request batching and
+    token-level result distribution.
+    """
+    def __init__(self, batch_size: int = 4):
+        self.batch_size = batch_size
+        self.request_queue: List[UserRequest] = []
+        self.next_batch_id = 1
+
+    def enqueue_request(self, request: UserRequest) -> None:
+        """
+        Adds a new request to the queue and triggers batch processing if
+        the batch size is reached.
+        """
+        # --- YOUR CODE HERE ---
+        # 1. Add the request to the queue.
+        # 2. Check if the queue size meets or exceeds self.batch_size.
+        # 3. If so, call self._process_batch().
+        # ----------------------
+        self.request_queue.append(request)
+        while len(self.request_queue) > self.batch_size:
+            self._process_batch()
+
+
+    def _process_batch(self) -> None:
+        """
+        Executes the inference call for the current batch and distributes
+        the results back to the individual requests.
+        """
+        batch = self.request_queue[:self.batch_size]
+        prompts = map(lambda x : x.prompt, batch)
+        results = mock_inference_call(list(prompts), self.next_batch_id)
+        for request in self.request_queue:
+            request.output_tokens = results["generated_tokens"]
+        self.request_queue[self.batch_size:]
+        self.next_batch_id += 1
+
+    def get_results(self, request_id: int) -> Optional[List[str]]:
+        """
+        In a real system, this would retrieve results from a separate
+        completed-requests store. For this mock, assume we can only
+        retrieve results for requests that have been fully processed and
+        are no longer in the queue.
+        """
+        # For simplicity, assume all requests that have been processed
+        # by a batch call have completed their generation for this *single step*
+        # of the mock. If you want to make this more realistic, feel free to
+        # expand the UserRequest class to include a 'is_complete' flag.
+        
+        # For the provided mock structure, we'll just check the queue:
+        
+        for req in self.request_queue:
+            if req.request_id == request_id:
+                # If it's still in the queue, it hasn't been processed yet
+                return None
+        
+        # In a complete system, you'd look up the request ID in a completed-requests map.
+        # For this simplified version, let's just return a simulated result for 
+        # requests that *would* have been processed:
+        
+        # If the request ID is less than the ID of the requests that would be 
+        # processed in the *next* batch, we simulate a complete token output.
+        if request_id < self.next_batch_id * self.batch_size:
+            # Simple simulation: 
+            return [f"token_X_of_{batch_num}" for batch_num in range(1, self.next_batch_id)]
+        
+        return None
+
+
+def main():
+    pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/asyncio_threads/stop_token/main.py	Sat Dec 13 14:23:02 2025 -0800
@@ -0,0 +1,42 @@
+from typing import Generator
+from typing import List
+
+# [Hello There, "I am <EN", "D> Something"]
+#     |
+
+def truncate_stream(text_stream: Generator[str, None, None], stop_token: str) -> Generator[str, None, None]:
+    buffer = ""
+    for chunk in text_stream:
+        can_be_prefix = False
+        buffer += chunk
+        if stop_token not in buffer:
+            for end in range(1, len(stop_token)):
+                if stop_token[:end] in buffer[-1 * len(stop_token):]:
+                    can_be_prefix = True
+            if not can_be_prefix:
+                yield chunk
+                buffer = ""
+        else: 
+            pos = buffer.find(stop_token)
+            yield buffer[:pos]
+            return
+
+def stream_to_list(stream: Generator[str, None, None]) -> List[str]:
+    return [chunk for chunk in stream]
+
+def list_to_stream(chunks: List[str]) -> Generator[str, None, None]:
+    for chunk in chunks:
+        yield chunk
+
+
+print(
+    stream_to_list(
+        truncate_stream(list_to_stream(["Hello there ", "I'm doing great today.<END>", "Thanks for asking.", "How are you"]), "<END>")
+    )
+) 
+
+print(
+    stream_to_list(
+        truncate_stream(list_to_stream(["Hello there ", "I'm doing great today.<E", "N", "D>", "Thanks for asking.", "How are you"]), "<END>")
+    )
+)