# 01. Python grammar often appeared in LangGraph

### TypedDict <a href="#typeddict" id="typeddict"></a>

`dict` Wow `TypedDict` Difference between `TypedDict` Why `dict` I'll explain if it is used instead.

1. `dict` Wow `TypedDict` The main difference
   * **Type inspection**
     * `dict` : No type check at runtime.
     * `TypedDict` : Provides static type checking. This means that when writing code, an IDE or type checker can catch the error in advance.
   * **Type of key and value**
     * `dict` : Normally specify the type of key and value (eg Dict\[str, str]).
     * `TypedDict` : You can specify specific types for each key.
   * **flexibility**
     * `dict` : You can add or remove keys at runtime.
     * `TypedDict` : You must follow the defined structure. Additional keys cause type errors.
2. `TypedDict` end `dict` Reasons to use instead
   * **Type stability**
     * `TypedDict` Can provide a stricter type check to prevent potential bugs in advance.
   * **Code readability**
     * `TypedDict` Using it clearly defines the structure of the dictionary, which improves the readability of the code.
   * **IDE support**
     * `TypedDict` With IDE, autocomplete and type hints can be provided more accurately.
   * **Documentation**
     * `TypedDict` The code itself acts as a document, clearly showing the structure of the dictionary.

```python
# Example of the difference between TypedDict and Dict
from typing import Dict, TypedDict

# Using a generic Python dictionary (dict)
sample_dict: Dict[str, str] = {
    "name": "테디",
    "age": "30",  # Save as string (possible with dict)
    "job": "개발자",
}


# Using TypedDict
class Person(TypedDict):
    name: str
    age: int  # Specify as integer
    job: str


typed_dict: Person = {"name": "Shirley", "age": 25, "job": "Designer"}
```

```python
# In the case of dict

sample_dict["age"] = 35  # No error when changing from string to integer
sample_dict["new_field"] = "추가 정보"  # New fields can be added


# For TypedDict
typed_dict["age"] = 35  # Correct use of integers
typed_dict["age"] = "35"  # Type checker detects an error
typed_dict["new_field"] = (
    "Additional information"  # Type checker throws error for undefined key
)
```

However, the true value of TypedDict is revealed when using static type checkers.

For example, if you use a static type checker like mypy, or if you enable type check functions in IDEs like PyCharm, VS Code, etc., it will error these type mismatches and adding undefined keys. The static type checker allows you to see the following error messages.

### Annotated <a href="#annotated" id="annotated"></a>

This grammar allows you to add metadata to type hints. `Annotated` Main reasons for using

**Provide additional information (type hints) / documentation**

* Additional information can be included in the type hint. This provides more context for people or tools reading code.
* You can include additional descriptions of the code directly in the type hint.

`name: Annotated[str, "이름"]`

`age: Annotated[int, "나이"]`

***

`Annotated` Is a special type hint provided by Python's typing module that allows you to add metadata to existing types.

`Annotated` Provides the ability to include additional information in the type hint. This allows you to increase the readability of your code and provide more detailed type information.

#### Annotated main function (reason for use) <a href="#annotated_1" id="annotated_1"></a>

1. **Provide additional information** : Add metadata to type hints to provide more detailed information.
2. **Documentation** : You can get a documentation effect by including additional explanations in the code itself.
3. **Validation** : You can use it with a specific library (e.g. Pydantic) to perform data validation.
4. **Framework support** : In some frameworks (e.g. LangGraph) `Annotated` Use to define special behaviors.

**Basic grammar**

* `Type` : Basic type (e.g. `int` , `str` , `List[str]` Etc)
* `metadata1` , `metadata2` , ...: Metadata you want to add

```python
from typing import Annotated

variable: Annotated[Type, metadata1, metadata2, ...]
```

#### Example of use <a href="#id-1" id="id-1"></a>

Basic use

```python
from typing import Annotated

name: Annotated[str, "user name"]
age: Annotated[int, "user age (0-150)"]
```

Used with Pydantic

```python
from typing import Annotated, List
from pydantic import Field, BaseModel, ValidationError


class Employee(BaseModel):
    id: Annotated[int, Field(..., description="직원 ID")]
    name: Annotated[str, Field(..., min_length=3, max_length=50, description="name")]
    age: Annotated[int, Field(gt=18, lt=65, description="age (19-64세)")]
    salary: Annotated[
        float, Field(gt=0, lt=10000, description="annual salary (Unit: ten thousand won, up to 1 billion won")
    ]
    skills: Annotated[
        List[str], Field(min_items=1, max_items=10, description="possession of technology (1-10개)")
    ]


# Create an instance with valid dataww
try:
    valid_employee = Employee(
        id=1, name="teddy note", age=30, salary=1000, skills=["Python", "LangChain"]
    )
    print("Valid employee data:", valid_employee)
except ValidationError as e:
    print("validation error:", e)

# Attempting to create an instance with invalid data
try:
    invalid_employee = Employee(
        name="테디",  # The name is too short
        age=17,  # Age is out of range
        salary=20000,  # Salary is out of range
        skills="Python",  # Not a listww
    )
except ValidationError as e:
    print("validation error:")
    for error in e.errors():
        print(f"- {error['loc'][0]}: {error['msg']}")
```

```
 Valid employee data: id=1 name='Tedinot' age=30 salary=1000.0 skills=['Python','LangChain'] 
Validation error: 
-id: Field required 
-name: String should have at last 3 characters 
-age: Input should be greater than 18 
-salary: Input should be less than 10000 
-skills: Input should be a valid list 
```

#### Use in LangGraph (add\_messages) <a href="#langgraph-add_messages" id="langgraph-add_messages"></a>

`add_messages` Is a function that adds a message to the list in LangGraph.

```python
from typing import Annotated, TypedDict
from langgraph.graph import add_messages


class MyData(TypedDict):
    messages: Annotated[list, add_messages]
```

```python
from typing import Annotated, TypedDict
from langgraph.graph import add_messages


class MyData(TypedDict):
    messages: Annotated[list, add_messages]
```

**Reference**

1. `Annotated` Available in Python 3.9 and above.
2. At runtime `Annotated` Since it is ignored, it does not affect the actual behavior.
3. Type inspection tool or IDE `Annotated` You need to support to see the effect.

### add\_messages <a href="#add_messages" id="add_messages"></a>

`messages` tall [`add_messages`](https://langchain-ai.github.io/langgraph/reference/graphs/?h=add+messages#add_messages) The reader function is annotated, which instructs LangGraph to add a new message to the existing list.

The state key without annotations is overwritten by each update to store the most recent values.

`add_messages` The function receives 2 factors (left, right) and acts in a way that merges left and right messages.

**Main features**

* Merge two message listings.
* It remains "append-only" by default.
* If you have a message with the same ID, replace the existing message with a new message.

**Motion method**

* `right` Message `left` If there is a message with the same ID, `right` Will be replaced by the message of.
* Other cases `right` Message `left` Is added to.

**parameter**

* `left` (Messages): Basic message list
* `right` (Messages): message list to merge or single message

**return value**

* `Messages` : `right` Messages `left` New message list merged with

```python
from langchain_core.messages import AIMessage, HumanMessage
from langgraph.graph import add_messages

# 기본 사용 예시
msgs1 = [HumanMessage(content="안녕하세요?", id="1")]
msgs2 = [AIMessage(content="nice to meet you~", id="2")]

result1 = add_messages(msgs1, msgs2)
print(result1)
```

```
 [HumanMessage (content='Hello?', additional_kwargs={}, response_metadata={}, id='1'), AIMessage (content=' Nice'TAG1>', additional_kwargs<TA1
```

If there is a Message with the same ID, it is replaced.

```python
# Example of replacing a message with the same ID
msgs1 = [HumanMessage(content="안녕하세요?", id="1")]
msgs2 = [HumanMessage(content="반갑습니다~", id="1")]

result2 = add_messages(msgs1, msgs2)
print(result2)
```

```
 [HumanMessage (content=' Nice to see ~', additional_kwargs={}, response_metadata={}, id='1')]
```

<br>
